python-telegram-bot/python-telegram-bot · warning · Warning

Unknown bot instance found. Will be replaced by `None` durin

Error message

Unknown bot instance found. Will be replaced by `None` during unpickling

What it means

PicklePersistence's persistent_id hook found a `Bot` instance that is not the persistence's own configured bot. Because Bot instances hold the event loop / HTTPX clients and can't be meaningfully pickled or matched to the running application, PTB replaces it with None when loading and warns so you know data changed.

Source

Thrown at src/telegram/ext/_picklepersistence.py:98

        self, obj: TelegramObj
    ) -> tuple[Callable, tuple[type[TelegramObj], dict]]:
        """
        This method is used for pickling. The bot attribute is preserved so
        _BotPickler().persistent_id works as intended.
        """
        if not isinstance(obj, TelegramObject):
            return NotImplemented

        return _custom_reduction(obj)

    def persistent_id(self, obj: object) -> str | None:
        """Used to 'mark' the Bot, so it can be replaced later. See
        https://docs.python.org/3/library/pickle.html#pickle.Pickler.persistent_id for more info
        """
        if obj is self._bot:
            return _REPLACED_KNOWN_BOT
        if isinstance(obj, Bot):
            warn(
                "Unknown bot instance found. Will be replaced by `None` during unpickling",
                stacklevel=2,
            )
            return _REPLACED_UNKNOWN_BOT
        return None  # pickles as usual


class _BotUnpickler(pickle.Unpickler):
    __slots__ = ("_bot",)

    def __init__(self, bot: Bot, *args: Any, **kwargs: Any):
        self._bot = bot
        super().__init__(*args, **kwargs)

    def persistent_load(self, pid: str) -> Bot | None:
        """Replaces the bot with the current bot if known, else it is replaced by :obj:`None`."""
        if pid == _REPLACED_KNOWN_BOT:
            return self._bot

View on GitHub (pinned to d3b69d2e9f)

Solutions

  1. Construct persistence with the bot: PicklePersistence(filepath=..., bot=application.bot) so the known bot is replaced correctly on load (set_bot is called by Application when it's built via ApplicationBuilder)
  2. Stop persisting objects that reference Bot: store bot-scoped values like chat_id/token strings instead of context.bot / context.application
  3. If loading legacy data, expect None where the unknown Bot was and re-set the bot at runtime after unpickling
  4. Check bot_data/user_data and any nested objects for accidental Bot references before update_persistence()

Example fix

# before
persistence = PicklePersistence(filepath="data")
# ...
async def callback(update, context):
    context.bot_data["bot"] = context.bot  # pickles an unknown Bot -> replaced by None

# after
persistence = PicklePersistence(filepath="data")  # bot set via ApplicationBuilder
# ...
async def callback(update, context):
    context.bot_data["chat_id"] = update.effective_chat.id  # store plain data only
Defensive patterns

Strategy: validation

Validate before calling

from telegram import Bot

def has_bot_reference(obj, _seen=None) -> bool:
    if isinstance(obj, Bot):
        return True
    if isinstance(obj, dict):
        return any(has_bot_reference(v) for v in obj.values())
    if isinstance(obj, (list, tuple, set)):
        return any(has_bot_reference(v) for v in obj)
    return any(
        has_bot_reference(v) for v in vars(obj).values()
    ) if hasattr(obj, "__dict__") and not isinstance(obj, type) else False

# before update_persistence / dump:
if has_bot_reference(application.bot_data):
    logging.warning("bot_data holds a Bot instance; it will unpickle as None")

Type guard

from telegram import Bot

def is_persistable(value) -> bool:
    """True if value (recursively) contains no Bot instance."""
    return not has_bot_reference(value)

Prevention

When it happens

Trigger: Creating `PicklePersistence(filepath=...)` without `bot=...` (or with a different Bot instance) and then pickling Update/CallbackContext/ChatData that (indirectly) references a Bot — e.g. storing `context.bot` in `bot_data`/`user_data`, or pickling objects whose attributes reach a Bot. Also when you build persistence for bot A but the stored object references bot B (two Application instances, tests with a fake bot).

Common situations: Storing context.bot or context.application (which holds bot) in persistence-backed dicts; running two bot instances sharing one persistence file; unit tests with mock bots while persistence was constructed with a different/real bot; forgetting to pass bot= when constructing PicklePersistence in PTB v20+ (where it's no longer set automatically).

Related errors


AI-assisted analysis of python-telegram-bot/python-telegram-bot@d3b69d2e9f (2026-08-28). Data as JSON: /api/errors/95c26fc4f15d67d0. Report an issue: GitHub.