fix: raise MessageParseError for non-dict message fields in parse_message#1090
Open
Otis0408 wants to merge 1 commit into
Open
fix: raise MessageParseError for non-dict message fields in parse_message#1090Otis0408 wants to merge 1 commit into
Otis0408 wants to merge 1 commit into
Conversation
…sage parse_message() documents `Raises: MessageParseError`, and existing tests (test_parse_assistant_string_content_raises, test_non_dict_content_block_raises_documented_error) already enforce that a malformed `content` value surfaces as MessageParseError, "never a raw TypeError". That guard only covered one nesting level, though: when the enclosing `message` (user/assistant), `rate_limit_info`, or `deferred_tool_use` object is present but is not a dict, the nested subscript (e.g. data["message"]["content"], info["status"], deferred["id"]) raised a raw TypeError that escaped the `except KeyError`-only handlers and reached the caller, bypassing the documented contract. Add isinstance guards mirroring the existing `isinstance(raw_content, list)` check so a non-dict `message` / `rate_limit_info` / `deferred_tool_use` raises a descriptive MessageParseError instead of a raw TypeError. Adds regression tests covering all four parse branches.
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_message()documentsRaises: MessageParseError: If parsing fails, and the maintainers already enforce this one level down:test_parse_assistant_string_content_raisesandtest_non_dict_content_block_raises_documented_errorassert that a malformedcontentvalue surfaces asMessageParseError, "never a raw TypeError".That guard stops at
content. When the enclosing object is present but is not adict, the nested subscript raises a rawTypeErrorthat escapes theexcept KeyError-only handlers and reaches the caller, violating the documented contract:main{"type": "user", "message": "hi"}TypeError: string indices must be integers{"type": "assistant", "message": None}TypeError: 'NoneType' object is not subscriptable{"type": "rate_limit_event", "rate_limit_info": "x", ...}TypeError(info["status"]){"type": "result", ..., "deferred_tool_use": "oops"}TypeError(deferred["id"])Because neither public call site wraps
parse_message(ClaudeSDKClient.receive_messages()/receive_response()and thequery()path), these reach SDK consumers as a rawTypeError, so callers thatexcept MessageParseError(or the exported baseClaudeSDKError) miss them.Fix
Add
isinstanceguards mirroring the existingisinstance(raw_content, list)check, so a non-dictmessage/rate_limit_info/deferred_tool_useraises a descriptiveMessageParseErrorinstead of a rawTypeError.except KeyErrorhandlers and their existing messages (asserted by tests) are untouched, so missing-field cases still report "Missing required field ...".tryblocks; sinceMessageParseErroris not aKeyErrorit propagates as documented (same as the currentcontentguard).Tests
Adds regression tests covering all four branches, parametrized over
str/list/None/intmessage values. Verified they fail onmainwith the rawTypeErrorand pass with the fix.