A 200 response from the Messages API is not one kind of event — it is several, distinguished by the stop_reason field on the returned message. Some values mean "done, ship it"; others mean "you have work to do before this response is usable". This article walks the values you will actually see and the correct branch for each.
The everyday four
end_turn — the model finished its turn naturally. This is the happy path: the content is complete, render or store it.
max_tokens — generation hit the max_tokens ceiling you set. The output is truncated, and if you were using structured outputs or tool calls, it may be incomplete or invalid JSON — the official guidance is to retry with a higher max_tokens. Remember that on thinking-enabled models, thinking tokens count toward max_tokens too, so a generous reasoning phase can starve the visible answer; raise the limit or lower effort. Treat max_tokens as an error state in pipelines: never persist a truncated answer as if it were complete.
stop_sequence — generation hit one of the custom stop strings you supplied in the request's stop_sequences parameter. The matched string is reported alongside so you can tell which one fired. This is deliberate truncation — your own delimiter logic — so it usually maps to "done", just by your rule rather than the model's.
tool_use — the model is asking your application to run one or more tools. The response contains tool_use content blocks; execute them, then send all results back as tool_result blocks in a single user message and call the API again. A response can contain several tool calls at once, and failures should go back with "is_error": true rather than being swallowed. Nothing here is final output — treating a tool_use response as an answer is one of the most common integration bugs.
The newer values your switch statement probably misses
| stop_reason | Meaning | Correct response |
|---|---|---|
pause_turn | A server-tool loop (web search, code execution) hit its iteration limit (default 10) | Re-send the conversation including the assistant response as-is; the server resumes automatically — do not append a "continue" message |
refusal | A safety refusal, returned with HTTP 200; on Claude Fable 5 a stop_details.category field says why (e.g. "cyber", "reasoning_extraction") | Do not retry verbatim in a loop; surface or log it. With structured outputs, note you are billed and output may not match the schema |
model_context_window_exceeded | On Claude 4.5+ models, input plus max_tokens overflowed the context window and generation stopped at the boundary | Trim context, or enable compaction / context editing |
compaction | You enabled compaction with pause_after_compaction: true and the API stopped after writing the summary | Handle continuation yourself, then resume the conversation |
A production-shaped branch
msg = client.messages.create(model="claude-opus-4-8", max_tokens=4096,
messages=history, tools=tools)
match msg.stop_reason:
case "end_turn" | "stop_sequence":
deliver(msg.content)
case "tool_use":
history += [assistant(msg), tool_results(run_tools(msg))]
case "pause_turn":
history.append(assistant(msg)) # resend unchanged
case "max_tokens" | "model_context_window_exceeded":
handle_truncation(msg) # never persist as complete
case "refusal":
log_refusal(msg) # don't blind-retry
Two habits make this robust. First, make the branch exhaustive with an explicit default that raises or alerts — new stop reasons have been added over time (refusal and model_context_window_exceeded arrived with the Claude 4 generation), and a silent fall-through is how truncation bugs ship. Second, when streaming, remember the final stop_reason arrives in the closing message_delta event, so your handler must not commit the response before the stream ends.
end_turn and your own stop_sequence mean "this content is finished". Everything else is a control-flow signal.Stop reasons behave the same across the Claude API, Claude Platform on AWS, Amazon Bedrock's Messages surface, Google Vertex AI, and Microsoft Foundry, since all speak the Messages API. Platform differences show up one level down — which features (server tools, compaction) can produce the newer values at all; check the availability matrix.
Where to go next
Stop reasons cover successful responses; error codes cover the failures. For the tool-use loop end to end, see tool choice, and for window overflow, 1M-token context strategies.