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._botView on GitHub (pinned to d3b69d2e9f)
Solutions
- 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)
- Stop persisting objects that reference Bot: store bot-scoped values like chat_id/token strings instead of context.bot / context.application
- If loading legacy data, expect None where the unknown Bot was and re-set the bot at runtime after unpickling
- 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
- Build persistence through ApplicationBuilder().persistence(PicklePersistence(filepath=...)) so set_bot wires the correct instance
- Store identifiers (chat_id, user_id, token) instead of Bot/Application objects in persisted dicts
- Never share one persistence file between two Application/Bot instances
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
- persistence must be based on telegram.ext.BasePersistence
- _TWO_ARGS_REQ.format(f"get_updates_{name}" if get_updates el
- _TWO_ARGS_REQ.format("bot", error)
- You can not assign a new value to update_interval after init
- callback_data can only be stored when using telegram.ext.Ext
AI-assisted analysis of python-telegram-bot/python-telegram-bot@d3b69d2e9f (2026-08-28).
Data as JSON: /api/errors/95c26fc4f15d67d0.
Report an issue: GitHub.