Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/claude_agent_sdk/_internal/message_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ def parse_message(data: dict[str, Any]) -> Message | None:
if deferred
else None,
errors=data.get("errors"),
api_error_status=data.get("api_error_status"),
uuid=data.get("uuid"),
)
except KeyError as e:
Expand Down
4 changes: 4 additions & 0 deletions src/claude_agent_sdk/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -1158,6 +1158,10 @@ class ResultMessage:
permission_denials: list[Any] | None = None
deferred_tool_use: DeferredToolUse | None = None
errors: list[str] | None = None
# HTTP status code (e.g. 429, 500, 529) of the failing API call when
# ``is_error`` is True and ``subtype`` is "success"; None otherwise.
# Emitted by the CLI since v2.1.110. Safe to log (no message content).
api_error_status: int | None = None
uuid: str | None = None


Expand Down
25 changes: 25 additions & 0 deletions tests/test_message_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -918,6 +918,7 @@ def test_parse_result_message_optional_fields_absent(self):
assert message.permission_denials is None
assert message.deferred_tool_use is None
assert message.errors is None
assert message.api_error_status is None
assert message.uuid is None

def test_parse_result_message_with_deferred_tool_use(self):
Expand Down Expand Up @@ -974,6 +975,30 @@ def test_parse_result_message_with_errors(self):
assert message.subtype == "error_during_execution"
assert message.uuid == "err-uuid-789"

def test_parse_result_message_with_api_error_status(self):
"""ResultMessage surfaces api_error_status for failed API calls.

The CLI (v2.1.110+) emits api_error_status: number | null on the final
result message — the HTTP status of the failing API call when
is_error=True and subtype="success". This is the only safe-to-log
signal for classifying API failures (e.g. 429 vs 529).
"""
data = {
"type": "result",
"subtype": "success",
"duration_ms": 2000,
"duration_api_ms": 1500,
"is_error": True,
"num_turns": 1,
"session_id": "session_overload",
"api_error_status": 529,
}
message = parse_message(data)
assert isinstance(message, ResultMessage)
assert message.api_error_status == 529
assert message.is_error is True
assert message.subtype == "success"

def test_parse_result_message_success_no_errors(self):
"""Test that a successful result message has no errors field."""
data = {
Expand Down
Loading