python-telegram-bot/python-telegram-bot · error · RuntimeError
Can't build key for update without effective user!
Error message
Can't build key for update without effective user!
What it means
When a ConversationHandler is created with per_user=True, the conversation key includes update.effective_user.id. Updates that carry no user (rare, e.g. some channel posts or malformed updates) make key construction impossible, so RuntimeError is raised in _get_key during check_update.
Source
Thrown at src/telegram/ext/_handlers/conversationhandler.py:644
)
return out
def _get_key(self, update: Update) -> ConversationKey:
"""Builds the conversation key associated with the update."""
chat = update.effective_chat
user = update.effective_user
key: list[int | str] = []
if self.per_chat:
if chat is None:
raise RuntimeError("Can't build key for update without effective chat!")
key.append(chat.id)
if self.per_user:
if user is None:
raise RuntimeError("Can't build key for update without effective user!")
key.append(user.id)
if self.per_message:
if update.callback_query is None:
raise RuntimeError("Can't build key for update without CallbackQuery!")
if update.callback_query.inline_message_id:
key.append(update.callback_query.inline_message_id)
else:
key.append(update.callback_query.message.message_id) # type: ignore[union-attr]
return tuple(key)
async def _schedule_job_delayed(
self,
new_state: asyncio.Task,
application: "Application[Any, CCT, Any, Any, Any, JobQueue]",
update: Update,
context: CCT,View on GitHub (pinned to d3b69d2e9f)
Solutions
- Set per_user=False for handlers that must process channel posts
- Restrict the update types the application fetches (Application.builder().updates(...)) or use a dedicated TypeHandler/channel_post handler instead of the conversation
- Use per_chat=True keyed conversations for channel content
Example fix
# before ch = ConversationHandler(..., per_user=True) # receives channel posts # after ch = ConversationHandler(..., per_user=False, per_chat=True) # or exclude channel posts: Application.builder().updates(Update.ALL_TYPES[:15]).build()
Defensive patterns
Strategy: validation
Validate before calling
def can_handle(ch, update):
return not (ch.per_user and update.effective_user is None) Type guard
def has_effective_user(update: Update) -> bool:
return update.effective_user is not None Prevention
- Don't route channel posts into per_user conversations
- Handle channel posts with dedicated channel_post handlers
When it happens
Trigger: ConversationHandler(per_user=True) processing an Update where effective_user is None — typically channel_post updates from anonymous channel admins or edited channel posts. Raised inside _get_key called from check_update.
Common situations: Handling channel posts or edited channel posts in a per_user conversation. Channel posts authored by channels themselves have sender_chat instead of from_user, leaving effective_user None.
Related errors
- Can't build key for update without effective chat!
- You can not assign a new value to map_to_parent after initia
- This handler is not persistent, has no name or the applicati
- Can't build key for update without CallbackQuery!
- callback_data_json is not in the required format
AI-assisted analysis of python-telegram-bot/python-telegram-bot@d3b69d2e9f (2026-08-28).
Data as JSON: /api/errors/6ebe7f4e49124095.
Report an issue: GitHub.