{"record":{"id":"6ebe7f4e49124095","repo":"python-telegram-bot/python-telegram-bot","slug":"can-t-build-key-for-update-without-effective-user","errorCode":null,"errorMessage":"Can't build key for update without effective user!","messagePattern":"Can't build key for update without effective user!","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/telegram/ext/_handlers/conversationhandler.py","lineNumber":644,"sourceCode":"            )\n\n        return out\n\n    def _get_key(self, update: Update) -> ConversationKey:\n        \"\"\"Builds the conversation key associated with the update.\"\"\"\n        chat = update.effective_chat\n        user = update.effective_user\n\n        key: list[int | str] = []\n\n        if self.per_chat:\n            if chat is None:\n                raise RuntimeError(\"Can't build key for update without effective chat!\")\n            key.append(chat.id)\n\n        if self.per_user:\n            if user is None:\n                raise RuntimeError(\"Can't build key for update without effective user!\")\n            key.append(user.id)\n\n        if self.per_message:\n            if update.callback_query is None:\n                raise RuntimeError(\"Can't build key for update without CallbackQuery!\")\n            if update.callback_query.inline_message_id:\n                key.append(update.callback_query.inline_message_id)\n            else:\n                key.append(update.callback_query.message.message_id)  # type: ignore[union-attr]\n\n        return tuple(key)\n\n    async def _schedule_job_delayed(\n        self,\n        new_state: asyncio.Task,\n        application: \"Application[Any, CCT, Any, Any, Any, JobQueue]\",\n        update: Update,\n        context: CCT,","sourceCodeStart":626,"sourceCodeEnd":662,"githubUrl":"https://github.com/python-telegram-bot/python-telegram-bot/blob/d3b69d2e9fb7af6c796f84516cfda751752c7cb7/src/telegram/ext/_handlers/conversationhandler.py#L626-L662","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nch = ConversationHandler(..., per_user=True)  # receives channel posts\n\n# after\nch = ConversationHandler(..., per_user=False, per_chat=True)\n# or exclude channel posts: Application.builder().updates(Update.ALL_TYPES[:15]).build()","handlingStrategy":"validation","validationCode":"def can_handle(ch, update):\n    return not (ch.per_user and update.effective_user is None)","typeGuard":"def has_effective_user(update: Update) -> bool:\n    return update.effective_user is not None","tryCatchPattern":null,"preventionTips":["Don't route channel posts into per_user conversations","Handle channel posts with dedicated channel_post handlers"],"tags":["python","telegram-bot","conversationhandler","channel-posts","key-derivation"],"backgroundTag":"missing-context-in-update","analyzedSha":"d3b69d2e9fb7af6c796f84516cfda751752c7cb7","analyzedAt":"2026-08-28T16:18:54.805Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}