Turn records

What a read or a wait of a turn returns: a TurnState, one of six records, and the values that the calls which start, steer or interrupt a turn return.

Guides: Turns and Session statuses.

NameWhat it is
TurnStateA turn's state as of cursor: one of the six records Running, Blocked, Completed, Interrupted, Failed and UnknownOutcome.
RunningThe turn is working.
BlockedThe turn waits for an answer to one or more callbacks.
CompletedThe turn completed, possibly early: see stop_reason.
InterruptedThe turn stopped before it completed.
FailedThe turn ended with an error, for example a refusal from the model provider.
UnknownOutcomeThe turn ended with an outcome added after your SDK version, such as "future_outcome".
ProtocolErrorAn error from where the session runs, such as the one a turn failed with.
InvalidOutputThe turn completed, but its output did not validate against the requested type.
ToolCallOne tool call the model made during the turn.
TokenUsageThe tokens one model call used, as the provider reports them.
OpenCallbackA callback that the turn, or one of its subagents, waits on: an OpenApproval, an OpenSignIn, an OpenClientToolCall or an UnknownOpenCallback.
OpenApprovalAn approval the turn waits on.
OpenClientToolCallA client tool call the turn waits on.
OpenSignInA sign-in the turn waits on: the agent asks the user to connect one or more sources, such as connectors, before it uses their tools.
UnknownOpenCallbackA callback of a kind with no type of its own in your SDK version.
BusyA new turn was not started, because another turn of the session is active.
TurnConflictA request named a turn that is no longer the session's active turn.
TurnState

TurnState

type TurnState[T] = Running | Blocked | Completed[T] | Interrupted | Failed | UnknownOutcome

A turn's state as of cursor: one of the six records Running, Blocked, Completed, Interrupted, Failed and UnknownOutcome.

Running

Running

class Running(*, session_id: str, turn_id: str, cursor: str, text: str | None)

The turn is working. Nothing waits on you.

ParameterTypeDefaultDescription
session_idstrrequiredThe ID of the turn's session.
turn_idstrrequiredThe ID of the turn.
cursorstrrequiredThe ID of the position in the session's log that the record was read at. For a Running or Blocked that a wait with on_event returns, it is the position the handler's events reached, when that differs.
textstr | NonerequiredThe turn's latest assistant text at that position, or None before the first one.
Blocked

Blocked

class Blocked(
    *,
    session_id: str,
    turn_id: str,
    cursor: str,
    text: str | None,
    callbacks: tuple[OpenCallback, ...],
)

The turn waits for an answer to one or more callbacks.

ParameterTypeDefaultDescription
session_idstrrequiredThe ID of the turn's session.
turn_idstrrequiredThe ID of the turn.
cursorstrrequiredThe ID of the position in the session's log that the record was read at. For a Running or Blocked that a wait with on_event returns, it is the position the handler's events reached, when that differs.
textstr | NonerequiredThe turn's latest assistant text at that position, or None before the first one.
callbackstuple[OpenCallback, ...]requiredThe callbacks the turn waits on.
Completed

Completed

class Completed[T](
    *,
    session_id: str,
    turn_id: str,
    cursor: str,
    text: str | None,
    output: T | InvalidOutput | None,
    stop_reason: str | None = None,
    tool_calls: tuple[ToolCall, ...] = (),
)

The turn completed, possibly early: see stop_reason.

With output_type, output is None when the turn ended early, so there is no answer to parse. Without output_type, it is the turn's text, also when the turn ended early (a skipped turn's text is the hook's reason, a cut-off turn's text is the cut-off reply), or "" when there is none.

ParameterTypeDefaultDescription
session_idstrrequiredThe ID of the turn's session.
turn_idstrrequiredThe ID of the turn.
cursorstrrequiredThe ID of the position in the session's log that the record was read at. For a Running or Blocked that a wait with on_event returns, it is the position the handler's events reached, when that differs.
textstr | NonerequiredThe turn's latest assistant text at that position, or None before the first one.
outputT | InvalidOutput | NonerequiredThe turn's text; with output_type, that text parsed, InvalidOutput when it does not validate, or None when the turn ended early.
stop_reasonstr | NoneNone"iteration_limit" (the turn reached its iteration limit), "skipped" (a hook skipped it), "rejected" (a hook rejected the reply), "output_limit" (the model's last reply stopped at the output token limit, after the agent asked it for a shorter reply up to 6 times), None when the turn ended normally, or a stop reason added after your SDK version.
tool_callstuple[ToolCall, ...]()The turn's own tool calls, in the order the session history records them; a subagent's calls are not included.
Interrupted

Interrupted

class Interrupted(*, session_id: str, turn_id: str, cursor: str, text: str | None, cause: str)

The turn stopped before it completed.

When no cause is given, the SDK reads "cancelled" if the turn ended with its cancelled session, else "interrupted". text is what the turn said before it stopped.

ParameterTypeDefaultDescription
session_idstrrequiredThe ID of the turn's session.
turn_idstrrequiredThe ID of the turn.
cursorstrrequiredThe ID of the position in the session's log that the record was read at. For a Running or Blocked that a wait with on_event returns, it is the position the handler's events reached, when that differs.
textstr | NonerequiredThe turn's latest assistant text at that position, or None before the first one.
causestrrequiredWhy the turn stopped: "interrupted" (a client interrupted the turn), "cancelled" (a client cancelled or closed the turn's session), "lifetime_limit" or "idle_limit" (the session reached one of its limits), or a cause added after your SDK version.
Failed

Failed

class Failed(*, session_id: str, turn_id: str, cursor: str, text: str | None, error: ProtocolError)

The turn ended with an error, for example a refusal from the model provider.

ParameterTypeDefaultDescription
session_idstrrequiredThe ID of the turn's session.
turn_idstrrequiredThe ID of the turn.
cursorstrrequiredThe ID of the position in the session's log that the record was read at. For a Running or Blocked that a wait with on_event returns, it is the position the handler's events reached, when that differs.
textstr | NonerequiredThe turn's latest assistant text at that position, or None before the first one.
errorProtocolErrorrequiredWhy the turn failed, a ProtocolError.
UnknownOutcome

UnknownOutcome

class UnknownOutcome(
    *,
    session_id: str,
    turn_id: str,
    cursor: str,
    text: str | None,
    outcome: str,
    raw: dict[str, JsonValue] = {},
)

The turn ended with an outcome added after your SDK version, such as "future_outcome".

Leave a case for this record, so your code still runs when outcomes are added.

ParameterTypeDefaultDescription
session_idstrrequiredThe ID of the turn's session.
turn_idstrrequiredThe ID of the turn.
cursorstrrequiredThe ID of the position in the session's log that the record was read at. For a Running or Blocked that a wait with on_event returns, it is the position the handler's events reached, when that differs.
textstr | NonerequiredThe turn's latest assistant text at that position, or None before the first one.
outcomestrrequiredThe outcome, such as "future_outcome".
rawdict[str, JsonValue]{}The turn's termination, unparsed.
ProtocolError

ProtocolError

class ProtocolError(
    *,
    message: str,
    code: str | None = None,
    retryable: bool = False,
    details: dict[str, JsonValue] | None = None,
)

An error from where the session runs, such as the one a turn failed with.

The code of a failed turn's error says what failed, so you can match on it:

  • "completion_rejected": the model provider refused the request with an HTTP 4xx status other than 401, 403, 408, 409 and 429, for example an unknown model or an invalid parameter: fix the request.
  • "completion_unauthorized": the model provider refused the credentials (HTTP 401 or 403). Not retryable.
  • "completion_rate_limited": more requests than your rate limit allows (HTTP 429 with reason rate_limited). Retryable.
  • "completion_unavailable": the model provider could not serve the request: no model capacity (HTTP 409, or any other 429), an outage or a server error. Retryable for a transient cause, such as HTTP 502, 503 or 504.
  • "completion_timed_out": the provider answered HTTP 408, the model stopped producing output, or the call ran past its time limit. Retryable, except for a call past its time limit.
  • "completion_context_too_large": the request is larger than the model's context window. Not retryable, except when the provider refuses it again right after a context compaction, or refuses even the smallest compaction request (details.reason is "after_compaction" or "minimal_summary_request").
  • "completion_billing_denied": billing refused the request, for example because the budget is spent. Not retryable.
  • "completion_failed": the model call failed without an HTTP status, for example a lost connection or a stream that ended early. Retryable.
  • "completion_failed_permanently": the model call failed without an HTTP status and a retry will not help, for example a reply that reports no token usage: an OpenAI-compatible server must support stream_options.include_usage. Not retryable.
  • "remote_session_failed": a served or deployed session failed or reached its time limit. The session has ended: start a new one.
  • "capability_execution_failed": a hook other than pre_tool_call or post_tool_call raised an exception, or its saved state was too large. The message ends with the exception's text. Not retryable.
  • "tool_raised": a tool raised an exception other than agents.ToolError on an agent with tool_errors="raise". The message names the tool and the exception's type. Not retryable.
  • "tool_output_schema_mismatch": a tool's result did not match its output_schema= on an agent with tool_errors="raise". Not retryable.
  • "invalid_compaction_summary": a context compaction failed: the model's summary was empty, had malformed <summary> delimiters, called tools, or reached the output token limit. Not retryable.
  • "compaction_request_too_large": a context compaction could not start, because the context it must send does not fit under the compaction threshold. Not retryable.
  • "compaction_replacement_too_large": the summary and the context the turn needs do not fit under the compaction threshold. Not retryable.
  • "turn_failed": a served agent's error that has no code of its own.
ParameterTypeDefaultDescription
messagestrrequiredWhat went wrong.
codestr | NoneNoneThe error's code, such as "completion_rate_limited", or None when it has none.
retryableboolFalseTrue when sending the same request again may succeed, for example after a model stream timeout.
detailsdict[str, JsonValue] | NoneNoneThe structured data that came with the error, such as {"reason": "invalid_request_error", "status_code": 422} from the model provider, or None when none came (data that is not a JSON object is left out).
InvalidOutput

InvalidOutput

class InvalidOutput(*, raw: JsonValue, error: str)

The turn completed, but its output did not validate against the requested type.

ParameterTypeDefaultDescription
rawJsonValuerequiredThe turn's text.
errorstrrequiredWhy it did not validate.
ToolCall

ToolCall

class ToolCall(name: str, arguments: JsonValue)

One tool call the model made during the turn.

name is the tool's full ID, such as client.get_weather, not the name the model calls. A call to a tool that does not exist keeps the model's name. In the tool_calls of a post_llm_call or post_agent_turn hook, the call is not resolved yet, so name is the name the model called, such as get_weather. A call that did not run is listed too: a permission rule denied it, a user rejected it at approval, its approval timed out, a hook skipped it or failed on it, the agent refused it (invalid_tool_call: arguments that are not valid JSON or that the schema rejects, an unknown tool, wait beside other calls), or, inside run_typescript, its arguments did not match the tool's input schema or the program's time budget stopped it before it started. arguments is the call's arguments: in Completed.tool_calls, the ones a pre_tool_call hook set, else the model's; in a hook's tool_calls, the model's. Arguments that are not valid JSON are the raw string, and a session of a deployed agent may report any JSON value.

ParameterTypeDefaultDescription
namestrrequiredThe tool's full ID, such as client.get_weather, not the name the model calls. A call to a tool that does not exist keeps the model's name.
argumentsJsonValuerequiredThe call's arguments: in Completed.tool_calls, the ones a pre_tool_call hook set, else the model's; in a hook's tool_calls, the model's. Arguments that are not valid JSON are the raw string, and a session of a deployed agent may report any JSON value.
TokenUsage

TokenUsage

class TokenUsage(
    *,
    input_tokens: int,
    output_tokens: int,
    total_tokens: int,
    cached_input_tokens: int = 0,
    cache_write_tokens: int = 0,
    reasoning_tokens: int = 0,
)

The tokens one model call used, as the provider reports them.

total_tokens is input_tokens plus output_tokens. cached_input_tokens counts the input tokens read from the provider's cache, and is part of input_tokens.

ParameterTypeDefaultDescription
input_tokensintrequiredThe tokens the model read, cached ones included.
output_tokensintrequiredThe tokens the model wrote.
total_tokensintrequiredinput_tokens plus output_tokens.
cached_input_tokensint0The input tokens read from the provider's cache, part of input_tokens.
cache_write_tokensint0The input tokens the provider wrote to its cache, as it reports them.
reasoning_tokensint0The output tokens the model spent reasoning, as the provider reports them.
Open callbacks

Open callbacks

OpenCallback

OpenCallback

class OpenCallback(*, id: str, session_id: str, turn_id: str, kind: str, title: str | None)

A callback that the turn, or one of its subagents, waits on: an OpenApproval, an OpenSignIn, an OpenClientToolCall or an UnknownOpenCallback.

turn.answer takes its id on any turn of its session or of a session above it in the tree. A subagent's callback has the subagent's IDs, its own turn included.

ParameterTypeDefaultDescription
idstrrequiredThe callback's ID, the one turn.answer takes.
session_idstrrequiredThe ID of the session that opened the callback: a subagent's, for a subagent's callback.
turn_idstrrequiredThe ID of the turn that opened the callback.
kindstrrequired"approval", "sign_in" or "client_tool"; other kinds are for Mistral's own use.
titlestr | NonerequiredAn approval's title, None for the other kinds.
OpenApproval

OpenApproval

class OpenApproval(
    *,
    id: str,
    session_id: str,
    turn_id: str,
    kind: str,
    title: str | None,
    request: ApprovalRequest,
)

An approval the turn waits on.

Inherits id, session_id, turn_id, kind and title from OpenCallback.

ParameterTypeDefaultDescription
requestApprovalRequestrequiredThe tool call that the approval is for: the same ApprovalRequest an on_approval handler gets.
OpenClientToolCall

OpenClientToolCall

class OpenClientToolCall[P, R](
    *,
    id: str,
    session_id: str,
    turn_id: str,
    kind: str,
    title: str | None,
    call: ClientToolCall,
)

A client tool call the turn waits on.

Check which tool it calls with <stub>.called(callback), then answer it with turn.answer(callback.id, await callback.run(fn)).

Inherits id, session_id, turn_id, kind and title from OpenCallback.

ParameterTypeDefaultDescription
callClientToolCallrequiredThe call to answer: the same ClientToolCall an on_client_tool handler gets.
OpenClientToolCall.run

OpenClientToolCall.run

async def run(
    fn: Callable[P, R | ToolResult[Any] | Awaitable[R | ToolResult[Any]]],
    /,
) -> R | ToolResult[Any]

Call fn with the call's arguments, validated by the tool's stub, and return its result: what turn.answer(callback.id, ...) takes for this call.

fn takes the stub's parameters, without a context, and returns the stub's return type or an agents.ToolResult, synchronously or not. A synchronous fn runs in a tool thread. An exception fn raises propagates: tool_errors does not apply.

When the model's arguments do not match the stub, fn is not called: the result is an error agents.ToolResult saying why, so that answering it lets the model correct the call.

ParameterTypeDefaultDescription
fnCallable[P, R | ToolResult[Any] | Awaitable[R | ToolResult[Any]]]requiredThe function that runs the tool on this side, with the stub's parameters.
Raises
SdkErrorNo stub for this tool in the agent's tools, guardrails, hooks or subagents (as on a handle from Agent.load_from()), or two stubs share its ID. Read call.arguments instead.
OpenSignIn

OpenSignIn

class OpenSignIn(
    *,
    id: str,
    session_id: str,
    turn_id: str,
    kind: str,
    title: str | None,
    requests: tuple[SignInRequest, ...],
)

A sign-in the turn waits on: the agent asks the user to connect one or more sources, such as connectors, before it uses their tools.

Answer it with turn.answer(callback.id, agents.SignInAnswer(connected=..., denied=...)), naming each source by its id, or with an on_sign_in= handler, which gets this record. kind is "sign_in". on_client_tool never gets a sign-in.

Inherits id, session_id, turn_id, kind and title from OpenCallback.

ParameterTypeDefaultDescription
requeststuple[SignInRequest, ...]requiredThe sources to connect, one SignInRequest each.
UnknownOpenCallback

UnknownOpenCallback

class UnknownOpenCallback(
    *,
    id: str,
    session_id: str,
    turn_id: str,
    kind: str,
    title: str | None,
    raw: dict[str, JsonValue] = {},
)

A callback of a kind with no type of its own in your SDK version.

A later SDK version can give a kind a type of its own. Match on kind, and leave open the kinds that you do not know.

Inherits id, session_id, turn_id, kind and title from OpenCallback.

ParameterTypeDefaultDescription
rawdict[str, JsonValue]{}The callback's data, unparsed.
Refusals

Refusals

Busy

Busy

class Busy(*, session_id: str, active_turn_id: str, turn: Turn)

A new turn was not started, because another turn of the session is active.

This is not a turn state: no turn was started.

ParameterTypeDefaultDescription
session_idstrrequiredThe ID of the session.
active_turn_idstrrequiredThe ID of the turn that is active.
turnTurnrequiredThe active turn's handle, to steer, interrupt or wait for.
TurnConflict

TurnConflict

class TurnConflict(*, session_id: str, expected_turn_id: str, active_turn_id: str | None)

A request named a turn that is no longer the session's active turn. Nothing was applied: the session refused the request.

ParameterTypeDefaultDescription
session_idstrrequiredThe ID of the session.
expected_turn_idstrrequiredThe ID of the turn that the request named.
active_turn_idstr | NonerequiredThe ID of the turn that is active now, or None when no turn is.