fix(parser): wrap non-dict message and rate_limit_info in MessageParseError - #1213
Open
Amey-Thakur wants to merge 1 commit into
Open
fix(parser): wrap non-dict message and rate_limit_info in MessageParseError#1213Amey-Thakur wants to merge 1 commit into
Amey-Thakur wants to merge 1 commit into
Conversation
…eError parse_message wraps malformed frames in MessageParseError so callers can catch one documented exception and recover the offending frame. anthropics#1064 added that guard for non-dict assistant content and non-dict content blocks, and the rate_limit_event guards (anthropics#599/anthropics#601/anthropics#689) covered missing keys. Two sibling shapes were still unguarded: a message that is not a dict (the assistant and user branches index data["message"]["content"] before any type check) and a rate_limit_info that is not a dict (info["status"]). Both raised a raw TypeError that escaped parse_message and tore down the receive_messages()/query() generator, the same failure mode anthropics#1064 describes. Anthropic-compatible backends that emit slightly different shapes are the cited trigger. Guard both with the isinstance check and MessageParseError wording already used elsewhere in the module, and add regression tests for non-dict message (assistant/user) and non-dict rate_limit_info.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
parse_messagewraps malformed frames inMessageParseErrorso callers can catch one documented exception and recover the offending frame fromerror.data. Two frame shapes slipped past that contract and raised a rawTypeErrorinstead, which escapesparse_messageand tears down thereceive_messages()/query()async generator.This continues the hardening from #1064 (non-dict assistant
contentand non-dict content blocks) and therate_limit_eventfield guards (#599 / #601 / #689, which covered missing keys). The two sibling shapes below were still unguarded.The gap
Both the
userandassistantbranches indexdata["message"]["content"]before checking thatmessageis a dict, and therate_limit_eventbranch readsinfo["status"]before checking thatrate_limit_infois a dict.Because the
TypeErroris not wrapped inMessageParseError, a consumer catchingMessageParseErrordoes not catch it, the raw frame is not attached for diagnosis, and the message stream is torn down. This is the same failure mode #1064 describes, and as that issue notes, Anthropic-compatible backends that emit slightly different frame shapes are the common trigger.Fix
Add the
isinstance(..., dict)guard, with the sameMessageParseErrorwording already used for non-dict content blocks, at the two sites that were missing it:user/assistant: validatedata["message"]is a dict before indexing into it.rate_limit_event: validatedata["rate_limit_info"]is a dict before reading its keys.Every frame that already parsed correctly is unchanged; only the three previously-crashing shapes now raise the documented
MessageParseErrorinstead of a rawTypeError.Tests
tests/test_message_parser.py:test_non_dict_message_raises_documented_error— parametrized overassistant/userand over a string, int, list, andNonemessage.test_non_dict_rate_limit_info_raises_documented_error— parametrized over the same non-dict values.Both assert
MessageParseErrorand theexpected dictmessage. They fail with a rawTypeErrorwithout this change and pass with it. The fulltests/test_message_parser.pysuite is green (92 passed).ruff check,ruff format --check, andmypyon the changed files are clean.