`parse_message` raises raw `TypeError` instead of `MessageParseError` on non-dict nested payloads
Los mantenedores suelen responder en 2 días
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- Medio día
- Aptitud para principiantes
- 76/100
Línea de trabajo
Empieza en src/claude_agent_sdk/_internal/message_parser.py, en las ramas user, assistant, result y rate_limit_event, donde message, deferred_tool_use y rate_limit_info se indexan sin comprobar la forma. El reproductor del issue ejecuta parse_message con once tramas mal formadas. Ejecútalo y comprueba que cada una lanza MessageParseError con .data establecido, y que tests/test_message_parser.py sigue pasando.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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.
- Lenguaje dominante
- Python
- Estrellas
- 8.5k
- Forks
- 1.4k
- Merge medio
- 3 d 10 h
- PR fusionados (30 d)
- 13
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de anthropics/claude-agent-sdk-python
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
anthropics/claude-agent-sdk-python#1509 ·
Los mantenedores suelen responder en 2 días
-
bug good first issue
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
anthropics/claude-agent-sdk-python#1507 ·
Los mantenedores suelen responder en 2 días
-
`fork_session` hangs forever on a cyclic `parentUuid` chain (no cycle guard in `_build_fork_lines`)Abiertobug good first issue
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
anthropics/claude-agent-sdk-python#1506 ·
Los mantenedores suelen responder en 2 días
-
ClaudeSDKClient.connect() with an async iterable closes stdin, so follow-up query() failsPosiblemente ocupada @m4xkushnir la tomó hace 19 días. Abiertobug
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
anthropics/claude-agent-sdk-python#1443 ·
Los mantenedores suelen responder en 2 días
-
fork_session drops transcript entries containing U+2028/U+2029/U+0085 (str.splitlines), breaking the parentUuid chainPosiblemente ocupada @mangeshraut712 la tomó hace 19 días. Abiertobug good first issue
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
anthropics/claude-agent-sdk-python#1441 ·
Los mantenedores suelen responder en 2 días
Todos los issues de anthropics/claude-agent-sdk-python
Issues similares
-
Contributing instructionsAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
dbt-labs/spark-utils#51 ·
-
bug pydanty:is-working
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
pydantic/pydantic-ai#13444 ·
Los mantenedores suelen responder en 1 día
-
good first issue help wanted
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
-
good first issue help wanted
Dificultad 1/5 Menos de una hora Aptitud para principiantes 72/100
-
changelog investigate
Dificultad 1/5 Menos de una hora Aptitud para principiantes 68/100
ramnes/notion-sdk-py#502 ·