{"record":{"id":"caa90903e6cb42b2","repo":"python-telegram-bot/python-telegram-bot","slug":"can-t-build-key-for-update-without-callbackquery","errorCode":null,"errorMessage":"Can't build key for update without CallbackQuery!","messagePattern":"Can't build key for update without CallbackQuery!","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"src/telegram/ext/_handlers/conversationhandler.py","lineNumber":649,"sourceCode":"        \"\"\"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,\n        conversation_key: ConversationKey,\n    ) -> None:\n        try:\n            effective_new_state = await new_state\n        except Exception as exc:","sourceCodeStart":631,"sourceCodeEnd":667,"githubUrl":"https://github.com/python-telegram-bot/python-telegram-bot/blob/d3b69d2e9fb7af6c796f84516cfda751752c7cb7/src/telegram/ext/_handlers/conversationhandler.py#L631-L667","documentation":"When per_message=True, ConversationHandler keys conversations by the message the callback query is attached to. If an update being checked has no callback_query at all (e.g. a Message or InlineQuery update), the key cannot be built and RuntimeError is raised in _get_key.","triggerScenarios":"ConversationHandler(per_message=True) whose check_update is invoked for a non-callback update (any Message, EditedMessage, InlineQuery, etc.). The per_message branch unconditionally accesses update.callback_query and raises if it is None.","commonSituations":"Building a per_message conversation but adding entry points or state handlers that match plain messages (common mistake — per_message=True should use CallbackQueryHandler entry points only). Triggered from check_update whenever a text message arrives while the handler is in a state that matches it.","solutions":["Make sure entry_points and all state handlers are CallbackQueryHandler instances when per_message=True","Add a check_update guard: use a TypeHandler(CallbackQuery, ...) wrapper so only callback updates reach the conversation","If the conversation must handle both messages and callbacks, use per_message=False with per_chat/per_user instead"],"exampleFix":"# before\nch = ConversationHandler(\n    entry_points=[CommandHandler('start', start)],  # message handler + per_message\n    states={1: [CallbackQueryHandler(one)]},\n    per_message=True,\n)\n\n# after\nch = ConversationHandler(\n    entry_points=[CallbackQueryHandler(start, pattern='^start$')],\n    states={1: [CallbackQueryHandler(one)]},\n    per_message=True,\n)","handlingStrategy":"validation","validationCode":"def is_per_message_safe(update: Update) -> bool:\n    return update.callback_query is not None\n# only feed callback updates to per_message conversations","typeGuard":"def is_callback_update(update: Update) -> bool:\n    return update.callback_query is not None","tryCatchPattern":null,"preventionTips":["Use only CallbackQueryHandler in per_message conversations","Lint your states dict: assert all handlers are CallbackQueryHandler when per_message=True"],"tags":["python","telegram-bot","conversationhandler","callbackquery","per-message"],"backgroundTag":"missing-context-in-update","analyzedSha":"d3b69d2e9fb7af6c796f84516cfda751752c7cb7","analyzedAt":"2026-08-28T16:18:54.805Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}