Skip to main content
This guide helps you diagnose and fix common issues with your Telegram support bot.

Message thread not found error

This error occurs when the bot tries to send a message to a topic that no longer exists (closed or deleted).

How the bot handles it

The bot automatically detects and recovers from this error:
support_bot/topic_manager.py (lines 27-34)
def _is_thread_missing(err: TelegramBadRequest) -> bool:
    msg = (getattr(err, "message", None) or "").lower()
    return (
        "message thread not found" in msg
        or "message thread is not found" in msg
        or "thread not found" in msg
        or ("topic" in msg and "closed" in msg)
    )
When a thread is missing, the bot creates a new topic:
support_bot/topic_manager.py (lines 138-170)
except TelegramBadRequest as err:
    if not _is_thread_missing(err):
        try:
            await bot.send_message(
                chat_id=self._operator_group_id,
                message_thread_id=topic.topic_id,
                text=(
                    "Failed to copy the user's message.\n"
                    f"type={message.content_type}, message_id={message.message_id}\n"
                    f"error={getattr(err, 'message', str(err))}"
                ),
            )
        except Exception:
            pass
        return topic
    await self._db.deactivate_conversation(message.from_user.id)
    topic = await self.ensure_topic(bot, message.from_user)
    reply_params = None
    copy_result = await bot.copy_message(
        chat_id=self._operator_group_id,
        from_chat_id=message.chat.id,
        message_id=message.message_id,
        message_thread_id=topic.topic_id,
        reply_parameters=reply_params,
    )

Manual recovery

If you need to manually recover:
1

Check the database

Find conversations with missing topics:
sqlite3 support_bot.sqlite3 "SELECT user_id, topic_id FROM conversations WHERE active = 1;"
2

Deactivate old conversation

sqlite3 support_bot.sqlite3 "UPDATE conversations SET active = 0 WHERE user_id = <USER_ID>;"
3

User sends new message

When the user sends their next message, a new topic will be automatically created.
The bot handles this automatically - manual intervention is rarely needed.

Bot can’t create topics

If the bot can’t create topics in your operator group, it’s usually a permissions issue.

Required permissions

Your bot needs these permissions in the operator group:

Manage Topics

Create, edit, and close topics

Send Messages

Send messages in topics

Read Messages

Read operator responses

Delete Messages

Optional: Clean up bot messages

Fix permissions

1

Open group settings

In Telegram, open your operator group and tap the group name.
2

Find your bot

Scroll to “Members” and find your bot in the list.
3

Make bot an admin

Tap on the bot and select “Promote to Admin”.
4

Grant permissions

Enable these permissions:
  • Manage Topics
  • Send Messages
  • Delete Messages (optional)
5

Enable topics

In group settings, ensure “Topics” are enabled.

Verify setup

Test topic creation:
# The bot creates topics here
async def ensure_topic(self, bot: Bot, user: User) -> TopicRef:
    lock = await self._lock_for(user.id)
    async with lock:
        existing = await self._db.get_active_conversation(user.id)
        if existing:
            return TopicRef(user_id=user.id, topic_id=existing.topic_id)

        topic = await bot.create_forum_topic(
            chat_id=self._operator_group_id,
            name=_topic_name(user),
        )
        await self._db.set_conversation(user_id=user.id, topic_id=topic.message_thread_id, active=True)
From support_bot/topic_manager.py (lines 61-72).
The operator group MUST be a supergroup with topics enabled. Regular groups don’t support topics.

Blocked by user errors

This error occurs when a user has blocked your bot or deleted their Telegram account.

How the bot handles it

The operator handler catches this error and notifies operators:
support_bot/handlers/operator.py (lines 48-52)
except TelegramForbiddenError:
    await message.reply(
        "The user has blocked the bot or has not opened the chat with the bot."
    )
    return

What operators see

When an operator tries to reply, they’ll see:
The user has blocked the bot or has not opened the chat with the bot.

Solutions

Ask the user to:
  1. Search for your bot in Telegram
  2. Tap “Unblock” if blocked
  3. Send /start to reopen the conversation
Some users may send messages through group mentions or other means without starting a private chat. Ask them to:
  1. Open a private chat with your bot
  2. Send /start
Add custom tracking:
async def mark_user_blocked(db: Database, user_id: int) -> None:
    # Add a custom field to track blocked status
    await db.conn.execute(
        "UPDATE users SET blocked = 1 WHERE user_id = ?",
        (user_id,),
    )
    await db.conn.commit()

Database locked errors

SQLite database locks can occur with concurrent access.

How the bot prevents locks

The bot uses several strategies:

1. WAL mode

Write-Ahead Logging improves concurrency:
support_bot/db.py (lines 29-33)
async def connect(self) -> None:
    self._conn = await aiosqlite.connect(self._path)
    await self._conn.execute("PRAGMA journal_mode=WAL;")
    await self._conn.execute("PRAGMA foreign_keys=ON;")
    await self._conn.commit()

2. Transaction lock

A mutex prevents conflicting transactions:
support_bot/db.py (lines 27, 48-57)
self._tx_lock = asyncio.Lock()

@asynccontextmanager
async def transaction(self) -> Any:
    async with self._tx_lock:
        await self.conn.execute("BEGIN;")
        try:
            yield
        except BaseException:
            await self.conn.rollback()
            raise
        else:
            await self.conn.commit()

3. User-specific locks

Topic creation uses per-user locks:
support_bot/topic_manager.py (lines 50-59)
self._locks: dict[int, asyncio.Lock] = {}
self._locks_guard = asyncio.Lock()

async def _lock_for(self, user_id: int) -> asyncio.Lock:
    async with self._locks_guard:
        lock = self._locks.get(user_id)
        if lock is None:
            lock = asyncio.Lock()
            self._locks[user_id] = lock
        return lock

Troubleshooting database locks

1

Check for multiple instances

Ensure only one bot instance is running:
# For systemd
sudo systemctl status telegram-support-bot

# For Docker
docker ps | grep support-bot

# Check processes
ps aux | grep support_bot
2

Check WAL files

Verify WAL mode is enabled:
sqlite3 support_bot.sqlite3 "PRAGMA journal_mode;"
# Should output: wal
3

Clear WAL files

If needed, checkpoint the WAL:
sqlite3 support_bot.sqlite3 "PRAGMA wal_checkpoint(TRUNCATE);"
4

Increase timeout

If still seeing issues, increase the SQLite timeout:
async def connect(self) -> None:
    self._conn = await aiosqlite.connect(self._path, timeout=30.0)
    await self._conn.execute("PRAGMA journal_mode=WAL;")
    await self._conn.execute("PRAGMA foreign_keys=ON;")
    await self._conn.commit()

Topic creation race conditions

Multiple messages from the same user arriving simultaneously could create duplicate topics.

How the bot prevents races

User-specific locks ensure only one topic is created:
support_bot/topic_manager.py (lines 61-66)
async def ensure_topic(self, bot: Bot, user: User) -> TopicRef:
    lock = await self._lock_for(user.id)
    async with lock:
        existing = await self._db.get_active_conversation(user.id)
        if existing:
            return TopicRef(user_id=user.id, topic_id=existing.topic_id)
The lock ensures:
  1. Only one ensure_topic call runs at a time per user
  2. Database is checked for existing conversation first
  3. New topic is only created if none exists

Verify lock behavior

To test the locking:
# Send multiple messages rapidly as the same user
# Only one topic should be created
import asyncio

async def test_race_condition():
    tasks = []
    for _ in range(10):
        task = topics.ensure_topic(bot, user)
        tasks.append(task)
    results = await asyncio.gather(*tasks)
    
    # All results should have the same topic_id
    assert len(set(r.topic_id for r in results)) == 1
The locking mechanism is automatic - no configuration needed.

Getting your operator group ID

You need the operator group ID for the OPERATOR_GROUP_ID environment variable.

Method 1: Using a bot command

1

Add to your bot

Add this temporary handler:
@router.message(F.text == "/getgroupid")
async def get_group_id(message: Message) -> None:
    await message.reply(
        f"Chat ID: <code>{message.chat.id}</code>\n"
        f"Chat Type: {message.chat.type}"
    )
2

Send command

In your operator group, send /getgroupid
3

Copy the ID

The bot will reply with the chat ID (starts with -100).
4

Remove handler

Delete the temporary handler after getting your ID.

Method 2: Using @RawDataBot

1

Add RawDataBot

Add @RawDataBot to your operator group.
2

Send any message

Send any message in the group.
3

Copy chat ID

RawDataBot will reply with JSON data. Look for "chat":{"id":-100...}.
4

Remove bot

Remove @RawDataBot from your group.

Method 3: From bot logs

If your bot is already running, check the logs when it tries to send a message:
sudo journalctl -u telegram-support-bot | grep "operator_group_id"
The group ID must start with -100. Regular groups (starting with just -) don’t support topics.

Checking bot permissions

Verify your bot has the correct permissions programmatically.

Get bot info

The bot logs its info on startup:
support_bot/main.py (lines 50-51)
me = await bot.get_me()
log.info("Started as @%s (id=%s)", me.username, me.id)

Check admin status

Add a debug command:
@router.message(F.text == "/checkperms")
async def check_permissions(message: Message, bot: Bot) -> None:
    try:
        admins = await bot.get_chat_administrators(message.chat.id)
        bot_admin = next(
            (admin for admin in admins if admin.user.is_bot and admin.user.id == (await bot.get_me()).id),
            None
        )
        
        if bot_admin:
            perms = bot_admin.rights if hasattr(bot_admin, 'rights') else bot_admin
            await message.reply(
                f"Bot permissions:\n"
                f"Can manage topics: {getattr(perms, 'can_manage_topics', False)}\n"
                f"Can send messages: {getattr(perms, 'can_post_messages', False)}\n"
                f"Can delete messages: {getattr(perms, 'can_delete_messages', False)}"
            )
        else:
            await message.reply("Bot is not an admin in this chat.")
    except Exception as e:
        await message.reply(f"Error checking permissions: {e}")

Failed message copy errors

Sometimes messages can’t be copied between chats.

Common causes

Some messages are protected and can’t be copied. The bot handles this for text messages:
support_bot/topic_manager.py (lines 114-137)
except TelegramForbiddenError:
    if message.content_type == "text" and _message_has_links(message):
        try:
            sent = await bot.send_message(
                chat_id=self._operator_group_id,
                message_thread_id=topic.topic_id,
                text=message.text or "",
                entities=message.entities,
                link_preview_options=LinkPreviewOptions(is_disabled=True),
                reply_parameters=reply_params,
            )
        except TelegramForbiddenError:
            # Bot can't write to the group/topic — nothing else we can do here.
            pass
        except TelegramBadRequest:
            pass
    return topic
Some content types might not be supported. The bot logs these:
support_bot/topic_manager.py (lines 138-152)
except TelegramBadRequest as err:
    if not _is_thread_missing(err):
        try:
            await bot.send_message(
                chat_id=self._operator_group_id,
                message_thread_id=topic.topic_id,
                text=(
                    "Failed to copy the user's message.\n"
                    f"type={message.content_type}, message_id={message.message_id}\n"
                    f"error={getattr(err, 'message', str(err))}"
                ),
            )
        except Exception:
            pass
        return topic
Files larger than 20MB (photos) or 50MB (other files) can’t be copied. Consider adding size checks:
if message.photo:
    file = await bot.get_file(message.photo[-1].file_id)
    if file.file_size > 20 * 1024 * 1024:  # 20MB
        await message.answer("File too large. Please send files under 20MB.")
        return

Operator messages not reaching users

If operator replies aren’t being delivered to users, check these issues.

Verify topic association

The bot finds users by topic ID:
support_bot/handlers/operator.py (lines 24-26)
user_id = await db.find_user_id_by_topic(int(message.message_thread_id))
if user_id is None:
    return
From support_bot/db.py:
support_bot/db.py (lines 193-200)
async def find_user_id_by_topic(self, topic_id: int) -> int | None:
    cur = await self.conn.execute(
        "SELECT user_id FROM conversations WHERE topic_id = ? AND active = 1",
        (topic_id,),
    )
    row = await cur.fetchone()
    await cur.close()
    return int(row[0]) if row else None

Debug steps

1

Check database

Verify the conversation exists:
sqlite3 support_bot.sqlite3 "SELECT user_id, topic_id, active FROM conversations;"
2

Check topic ID

Right-click the topic in Telegram and check the URL. The topic ID is the last number.
3

Verify bot is responding

Check that the bot is processing operator messages:
sudo journalctl -u telegram-support-bot -f
4

Check message handler

Ensure the operator router is properly filtered:
support_bot/main.py (lines 47-48)
operator_router.message.filter(F.chat.id == config.operator_group_id)
dp.include_router(operator_router)

Common mistakes

  • Replying outside a topic (general chat) won’t work
  • Replying in a closed/deleted topic won’t work
  • Bot must be replying in the correct operator group
  • Topic must be associated with an active conversation
If reply chains aren’t maintained between users and operators, check message link logging. The bot creates bidirectional links:
support_bot/topic_manager.py (lines 193-218)
async def _log_message_link(
    self,
    *,
    user_id: int,
    source_chat_id: int,
    source_message_id: int,
    target_chat_id: int,
    target_message_id: int,
) -> None:
    async with self._db.transaction():
        await self._db.log_message_link(
            user_id=user_id,
            source_chat_id=source_chat_id,
            source_message_id=source_message_id,
            target_chat_id=target_chat_id,
            target_message_id=target_message_id,
            commit=False,
        )
        await self._db.log_message_link(
            user_id=user_id,
            source_chat_id=target_chat_id,
            source_message_id=target_message_id,
            target_chat_id=source_chat_id,
            target_message_id=source_message_id,
            commit=False,
        )
sqlite3 support_bot.sqlite3 "SELECT * FROM message_links LIMIT 10;"
Expected output:
id|user_id|source_chat_id|source_message_id|target_chat_id|target_message_id|created_at
1|123456|123456|1|-100123456|2|2024-01-01T00:00:00
2|123456|-100123456|2|123456|1|2024-01-01T00:00:00

Bot not starting

If your bot won’t start, check these common issues.

Check logs

# For systemd
sudo journalctl -u telegram-support-bot -n 50

# For Docker
docker logs support-bot --tail 50

Common startup errors

Error: Unauthorized or Invalid tokenSolution:
  1. Get a new token from @BotFather
  2. Update your .env file
  3. Restart the bot
Error: unable to open database fileSolution:
  1. Check the database path in .env
  2. Ensure the directory exists
  3. Check file permissions:
    chmod 600 support_bot.sqlite3
    chown telegram-bot:telegram-bot support_bot.sqlite3
    
Error: ModuleNotFoundErrorSolution:
  1. Activate your virtual environment
  2. Install dependencies:
    pip install -r requirements.txt
    
This shouldn’t happen as the bot doesn’t open any ports (uses long polling).If you see this error, check for other processes:
ps aux | grep support_bot
kill <PID>  # Kill the old process

Getting help

If you’re still experiencing issues:

Check the code

Review the source code in:
  • support_bot/main.py
  • support_bot/handlers/user.py
  • support_bot/handlers/operator.py
  • support_bot/topic_manager.py
  • support_bot/db.py

Enable debug logging

Set LOG_LEVEL=DEBUG in your .env file for detailed logs.

Test manually

Use the Python REPL to test database queries and bot methods interactively.

Check Telegram status

Visit Telegram Status to check for API issues.

Next steps

Deployment guide

Deploy your bot to production

Customization guide

Customize your bot’s behavior

Build docs developers (and LLMs) love