[callback] treat missing message context as no reply_to_message in template quick-reply#216
Conversation
WhatsApp does not always include the context field on incoming template quick-reply button presses (observed in production on Cloud API webhooks that also carry the new user_id/from_user_id fields). CallbackButton.from_update and CallbackSelection.from_update index msg["context"] unconditionally, so such updates raise KeyError: 'context' and are dropped by the server's update constructor — the handler never fires and, since 200 is returned, Meta never redelivers. Parse context the same way Message.from_update already does: treat it as optional and set reply_to_message=None when absent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Hi @WouterDurnez, thanks for the contribution and for adding the test cases! Just to help me understand the root cause better, could you share which version of the You can find this information in your app settings > WhatsApp > Configuration: Also, did you notice if there's a specific scenario that triggers Thanks again! |
|
Hi David, the The observed case was a template quick-reply button ( I included Thanks! |
|
Thanks for the clarification! That makes total sense. The reason this only happens for Lines 280 to 288 in 6ebd161 Since Let's go ahead and narrow the change down. Could you please:
Appreciate the thoroughness! Let me know when you've updated it. |
CallbackSelection only ever parses interactive list_reply payloads, which always carry the context field — only the button payload type (template QuickReplyButton clicks) omits it. Revert the defensive context parsing and synthetic fixture for CallbackSelection, and document on CallbackButton.reply_to_message (sync and async) that it is None specifically for template quick-reply clicks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Done in fbaadb6 👍
Full suite passes (158 passed). Thanks for the review! |
|
Awesome, thank you for the quick update and for cleaning that up! Everything looks perfectly dialed in now. |

The bug
WhatsApp Cloud API does not always include the
contextfield on incomingtype: "button"messages (template quick-reply presses). We hit this in production (EU WABA, 2026-07-13) with a payload that carried the neweruser_id/from_user_idfields but nocontext:{ "from": "32400000001", "from_user_id": "BE.0000000000000000", "id": "wamid.HBg...", "timestamp": "1783932397", "type": "button", "button": {"payload": "...", "text": "..."} }CallbackButton.from_update(andCallbackSelection.from_update) indexmsg["context"]unconditionally:The server catches this, logs
Failed to construct update, and still returns 200 — so Meta never redelivers and the user's button press is silently lost (the registered handler never fires).The fix
Parse
contextthe same wayMessage.from_updatealready does (message.py:content.get("context", {})→ReplyToMessage.from_dict(context) if context.get("id") else None): treat it as optional and setreply_to_message=Nonewhen absent. Annotations and docstrings updated toReplyToMessage | Noneaccordingly (matchingMessage.reply_to_message).Tests
Added
quick_reply_without_context/callback_without_contextupdate fixtures plusreply_to_messageassertions intest_updates.py. Without the fix, the new fixtures reproduce the production crash immediately (fixture construction intests/common.pyraisesKeyError: 'context'); with it, the full suite passes with no regressions.Thanks for the great library! 🙏