Skip to main content

Basic Usage

Built-in Stop Conditions

stepCountIs(n)

Stop after a specific number of steps:

hasToolCall(name)

Stop when a specific tool is called:

maxTokensUsed(n)

Stop after using a certain number of tokens:

maxCost(amount)

Stop after reaching a cost threshold:

finishReasonIs(reason)

Stop on a specific finish reason:

Combining Conditions

Pass an array to stop on any condition:
Execution stops when any condition is met.

Final Response After Stop

A stop condition can fire while the model is still emitting tool calls. By default, the SDK then executes those pending tool calls (so they have matching outputs) and makes one more model turn with toolChoice: 'none', appending a built-in final-answer directive (exported as DEFAULT_FINAL_RESPONSE_DIRECTIVE) as a user message — so the run ends with a real natural-language answer instead of a half-finished tool call. Tools stay in the request; only calling is forbidden, which preserves the prompt-cache prefix. Tune it with allowFinalResponse:
Without the directive, models that emit tool-call syntax as text can leak an unparsed tool call (e.g. <tool_call>…) into the final content when the loop stops mid-tool-call. The default directive exists to prevent exactly that.
The final turn only happens when a stop condition halted the loop while the last response contained executable tool calls. It never fires on natural completion, HITL/approval pauses, or interruption.

Empty final output after tool rounds

Some mini-class models intermittently treat a successful tool call as the terminal answer, returning an empty output array on the final turn. By default, the SDK retries that follow-up once, and if it’s still empty, resolves the run with empty text instead of throwing Invalid final response: empty or invalid output. This tolerance only applies after at least one completed tool execution round. Runs that produce no tool work still throw on an empty final response. Opt back into the strict contract with strictFinalResponse: true:
Set strictFinalResponse: true if your code depends on non-empty final text and you’d rather see an error than an empty completion. Leave it off (the default) to keep tool-terminal runs from being reported as failures.

Custom Stop Conditions

Create custom conditions with a function:

StopConditionContext

Custom functions receive:

StepResult

Each step contains:

Advanced Patterns

Time-Based Stopping

Stop after a time limit:

Content-Based Stopping

Stop based on response content:

Quality-Based Stopping

Stop when results meet quality threshold:

Combination with Early Exit

Combine conditions for complex logic:

Migration from maxToolRounds

If you were using maxToolRounds, migrate to stopWhen:

Default Behavior

If stopWhen is not specified, the loop runs until the model produces a turn with no tool calls. Always pass an explicit stopWhen (e.g. stepCountIs(n), maxCost(...)) to bound iterations, cost, or tokens.

Best Practices

Always Set Limits

Always include a hard limit to prevent runaway execution:

Log Stop Reasons

Track why execution stopped:

Test Conditions

Verify conditions work as expected:

See Also