Follow Discord
Sweep 22 Oct 2026 · 04:32Z Build v2.1.308 527 read Stable v2.1.308 Latest v2.1.308 Next v2.1.308 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One change · claude-code

Agent SDK reference - Python changedagent-sdk/python

Nearest release: v2.1.306, published under an hour after upstream edited the page. Shown because the two are within 24 hours of each other. Nothing here says the release caused the edit.

Upstream edited this page at 21 Oct 2026 01:41 UTC, give or take a minute or two: the time comes from Anthropic’s own sitemap rather than from a commit. This site recorded the change at 21 Oct 2026 02:07 UTC.

Upstream edited
Recorded here
Lines+13added
Lines−6removed
From line 1,711 where the diff opens
First seen 14 Aug 2026 this site's first read of the page
Recorded edits61to this page, all time

The whole hunk

from line 1711, old and new numbered
/
lines
from line 1711
17111711 
17121712Several fields carry diagnostic detail about how the conversation ended:
17131713 
1714* `is_error`: `True` when the conversation ended in an error state. Always `True` on the `error_*` subtypes. On `subtype="success"` it is `True` when the final model request failed, meaning the agent loop completed but the last API call returned an error.
1714* `is_error`: `True` when the conversation ended in an error state. Always `True` on the `error_*` subtypes. On `subtype="success"` it is `True` in three cases. Check `stop_reason` to tell them apart:
1715 * `"refusal"`: the model declined to respond. Read the refusal text in `result`, or in the preceding `AssistantMessage` when `result` is empty.
1716 * `"tool_deferred_unavailable"`: the [deferred tool](/docs/en/hooks#defer-a-tool-call-for-later) was no longer available when you resumed the session, for example because its MCP server wasn't connected. In the two cases below, a prompt you send after resuming counts the same as one passed with the resume:
1717 * No prompt: the call is still pending. Make the tool available and resume again without a prompt, and the call runs first.
1718 * A prompt: Claude Code answers it without the tool's result, and the deferred call is gone. Send a new prompt if you still need the tool called.
1719 * Any other value: the turn ended on an error that Claude Code reported in place of a model response, such as an API error. Read `terminal_reason` for the cause, `result` for the message, and `api_error_status` for the HTTP status when the API returned one.
17151720* `api_error_status`: the HTTP status code of the terminating API error. `None` when the turn ended without one. Populated only on `subtype="success"`.
17161721* `result`: text of the final assistant message on `subtype="success"`, or `None` on the `error_*` subtypes. When `subtype="success"` and `is_error=True`, this holds the API error string if one is available but can be empty, so check `api_error_status` and the preceding `AssistantMessage` content for detail.
17171722* `errors`: loop-level error strings such as the max-turns message. Populated only on the `error_*` subtypes.
from line 2063
20582063 errors: list[str] # an empty list when the result message reported none
20592064 result: str | None
20602065 api_error_status: int | None
2061 terminal_reason: str | None # "max_turns", "api_error", ...; check this before subtype
2066 terminal_reason: str | None # "max_turns", "api_error", ...; check after data["stop_reason"], before subtype
20622067 session_id: str | None
20632068 data: dict[str, Any] # the raw result message payload
20642069```
20652070 
2066To tell failures apart, check `terminal_reason` before `subtype`. When the final request fails, such as on an API error, Claude Code reports `subtype` `"success"` with the cause in `terminal_reason`, for example `"api_error"`; when a limit you set ends the run, such as `max_turns` or `max_budget_usd`, it reports an `error_*` subtype.
2071To tell failures apart, check `data["stop_reason"]` first, then `terminal_reason`, then `subtype`. A declined response has `stop_reason` `"refusal"`, usually with `subtype` `"success"`; its `terminal_reason` doesn't single it out and can read `"completed"` or `"api_error"`. A failed final request has `subtype` `"success"` and its cause in `terminal_reason`, such as `"api_error"` for an API error. When the run hits a limit you set, such as `max_turns` or `max_budget_usd`, you get an `error_*` subtype.
20672072 
20682073### `CLIJSONDecodeError`
20692074 
from line 3520
35153520 "Claude Code CLI not found. Try reinstalling: pip install --force-reinstall claude-agent-sdk"
35163521 )
35173522 # Catch ResultError before ProcessError, which it subclasses. Its message
3518 # carries the error text. A failed final request, such as an API error,
3519 # arrives with subtype "success", so branch on terminal_reason first.
3523 # carries the error text. A refusal and a failed final request both usually
3524 # arrive with subtype "success", so check stop_reason, then terminal_reason.
35203525 except ResultError as e:
3521 if e.terminal_reason == "api_error":
3526 if e.data.get("stop_reason") == "refusal":
3527 print(f"Model declined to respond: {e}")
3528 elif e.terminal_reason == "api_error":
35223529 print(f"API request failed: {e}")
35233530 else:
35243531 print(f"Query ended with an error result ({e.terminal_reason or e.subtype}): {e}")
Feedback