`parse_message` raises raw `TypeError` instead of `MessageParseError` on non-dict nested payloads
Maintainer thường phản hồi trong vòng 2 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- Nửa ngày
- Mức phù hợp với người mới
- 76/100
Hướng nghiên cứu
Bắt đầu từ src/claude_agent_sdk/_internal/message_parser.py, tại các nhánh user, assistant, result và rate_limit_event, nơi message, deferred_tool_use và rate_limit_info được truy cập bằng chỉ số mà không kiểm tra cấu trúc. Đoạn tái hiện trong issue chạy parse_message trên mười một khung dữ liệu sai định dạng. Hãy chạy nó và kiểm tra xem mỗi khung có ném MessageParseError với .data được đặt hay không, và tests/test_message_parser.py vẫn pass.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Summary
parse_message raises a raw TypeError instead of the documented MessageParseError when a frame's nested payload (message, deferred_tool_use, rate_limit_info) is present but not a dict, because each branch catches only KeyError. Callers catching ClaudeSDKError / MessageParseError do not catch it, so the message stream dies with an undocumented exception type and no .data payload.
This is a correctness/error-contract bug, not a security vulnerability: the frames come from the trusted local CLI subprocess.
Related: #1058 fixed the sibling case (string/non-dict content inside dict messages) in 0.2.111; this issue covers the distinct nested-payload layer (message / deferred_tool_use / rate_limit_info themselves non-dict), which still raises raw TypeError at the pinned commit.
Version
- Repo:
anthropics/claude-agent-sdk-python - Package:
claude-agent-sdk0.2.181 - Commit:
22b8eb9c19adfa78759b79e22bd664a57843bb06(main, 2026-11-12)
Location
src/claude_agent_sdk/_internal/message_parser.py. The docstring promises MessageParseError when parsing fails (:60-61), yet each branch catches only KeyError (user :146, assistant :221, result :338, rate_limit_event :373), while non-dict nested values fail at subscripting before any KeyError can arise:
- user :102 —
data["message"]["content"]withmessagea string/list/None - assistant :153 — same subscript
- result :325-329 —
deferred["id"]withdeferred_tool_usea string/list/bool (the guard is truthiness-only) - rate_limit :358-361 —
info["status"]withrate_limit_infoa string/list/None
Both call sites are unguarded (src/claude_agent_sdk/client.py:267-270; src/claude_agent_sdk/_internal/client.py:204-207), and Query.receive_messages yields raw dicts with no shape validation (src/claude_agent_sdk/_internal/query.py:1164-1176). MessageParseError subclasses ClaudeSDKError and carries .data (src/claude_agent_sdk/_errors.py:129-134). The no-raw-TypeError contract is pinned by tests asserting sibling malformed shapes raise MessageParseError (tests/test_message_parser.py:1002-1016).
All traced failures are TypeError only — no AttributeError path was observed from JSON-decoded input.
Minimal repro
from claude_agent_sdk._internal.message_parser import parse_message
from claude_agent_sdk import ClaudeSDKError
frames = [
{"type": "user", "message": "oops"},
{"type": "user", "message": []},
{"type": "user", "message": None},
{"type": "assistant", "message": []},
{"type": "assistant", "message": "oops"},
{"type": "result", "subtype": "success", "duration_ms": 1,
"duration_api_ms": 1, "is_error": False, "num_turns": 1,
"session_id": "sess-1", "deferred_tool_use": "x"},
{"type": "result", "subtype": "success", "duration_ms": 1,
"duration_api_ms": 1, "is_error": False, "num_turns": 1,
"session_id": "sess-1", "deferred_tool_use": [1]},
{"type": "result", "subtype": "success", "duration_ms": 1,
"duration_api_ms": 1, "is_error": False, "num_turns": 1,
"session_id": "sess-1", "deferred_tool_use": True},
{"type": "rate_limit_event", "rate_limit_info": "x"},
{"type": "rate_limit_event", "rate_limit_info": []},
{"type": "rate_limit_event", "rate_limit_info": None},
]
for frame in frames:
try:
parse_message(frame)
except ClaudeSDKError as exc:
print("MessageParseError:", exc)
except TypeError as exc:
print("RAW TypeError:", exc)
Observed: all 11 shapes raise raw TypeError (e.g. string indices must be integers, not 'str', 'NoneType' object is not subscriptable), which escapes an except ClaudeSDKError handler.
Expected: all 11 shapes raise MessageParseError with the raw data attached as .data, like the sibling malformed shapes (missing key, non-list assistant content, top-level non-dict) already do.
Impact
Applications that handle malformed CLI output by catching ClaudeSDKError / MessageParseError — the documented contract — miss these failures entirely: the stream dies with a raw TypeError and no .data payload for diagnostics. The exception-type inconsistency also makes the parser's error behavior unpredictable across malformed-frame shapes. Local error-handling impact only.
Suggested fix
Catch TypeError alongside KeyError in each parse_message branch (or validate that message / deferred_tool_use / rate_limit_info are dicts before subscripting) and raise MessageParseError with the raw data attached.
Environment
- Python 3.12.3, Linux
- Verified against the pinned commit above by driving
parse_messagedirectly with in-memory dicts (the exact valuesjson.loadswould produce from CLI stdout lines). Valid frames parse; valid-error frames already raiseMessageParseErrorwith.data. The CLI was not spawned and theQuerystream was not run; the unguarded call sites are established by code trace.
- Ngôn ngữ chính
- Python
- Star
- 8.5k
- Fork
- 1.4k
- Merge trung bình
- 3 ngày 10 giờ
- Pull request đã merge (30 ngày)
- 13
Chuẩn bị môi trường
Dự án này không cung cấp dev container, Dockerfile hay hướng dẫn đóng góp, nên bạn cần tự thiết lập môi trường: hãy bắt đầu từ README và xem hướng dẫn đóng góp lần đầu của chúng tôi để biết các bước chung.
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của anthropics/claude-agent-sdk-python
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
anthropics/claude-agent-sdk-python#1509 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
bug good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
anthropics/claude-agent-sdk-python#1507 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
`fork_session` hangs forever on a cyclic `parentUuid` chain (no cycle guard in `_build_fork_lines`)Đang mởbug good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
anthropics/claude-agent-sdk-python#1506 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
ClaudeSDKClient.connect() with an async iterable closes stdin, so follow-up query() failsCó thể đã có người làm @m4xkushnir đã nhận 19 ngày trước. Đang mởbug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
anthropics/claude-agent-sdk-python#1443 ·
Maintainer thường phản hồi trong vòng 2 ngày
-
fork_session drops transcript entries containing U+2028/U+2029/U+0085 (str.splitlines), breaking the parentUuid chainCó thể đã có người làm @mangeshraut712 đã nhận 19 ngày trước. Đang mởbug good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
anthropics/claude-agent-sdk-python#1441 ·
Maintainer thường phản hồi trong vòng 2 ngày
Tất cả issue của anthropics/claude-agent-sdk-python
Issue tương tự
-
Contributing instructionsĐang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
dbt-labs/spark-utils#51 ·
-
bug pydanty:is-working
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
pydantic/pydantic-ai#13444 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
good first issue help wanted
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
-
good first issue help wanted
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 72/100
-
changelog investigate
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 68/100
ramnes/notion-sdk-py#502 ·