What you will be able to do
- Check stop_reason first and handle end_turn, max_tokens, tool_use, pause_turn and refusal correctly
- Recover from truncated tool calls and refusals without reusing incomplete output
- Add an empty-content guard to a response-validation pipeline
- Use grounding and citation checks to test whether well-formed output is actually correct
1.Read stop_reason before the content
Every successful Messages API response has a stop_reason field that says why Claude stopped generating. It is not an error. Errors mean the request failed, while a stop reason describes a completed generation. A response can be a 200 and still be unusable as an answer. Have your pipeline branch on stop_reason before it reads the content. That one field decides whether you use the response, continue it, retry it or send it to another model.
| stop_reason | When it occurs | What to do |
|---|---|---|
| end_turn | Claude finished its response naturally. | Use the response. |
| max_tokens | The response reached your max_tokens limit. | Raise max_tokens or continue the response. |
| stop_sequence | Claude emitted one of your stop_sequences. | Read stop_sequence to see which one fired. |
| tool_use | Claude is calling a tool. | Run the tool and return the result. |
| pause_turn | A server-tool loop reached its iteration limit. | Send the assistant content back to continue. |
| refusal | Claude declined to respond. | Read stop_details and retry on a fallback model. |
| model_context_window_exceeded | The response filled the model's context window. | Treat the response as truncated. |
Checkpoint 1 of 7· Fill the gap
Which stop_reason means the content is complete and safe to process?
if response.stop_reason == " ? ":
# Process the complete response
for block in response.content:
if block.type == "text":
print(block.text)end_turn means Claude finished naturally. max_tokens and pause_turn mean the turn is unfinished, and stop_sequence means one of your own sequences cut it off.
Source: platform.claude.comSources1
2.Truncated, tool and paused turns
Three stop reasons mean the work isn't finished. With max_tokens and model_context_window_exceeded the output was cut off. Text that ends mid-thought is a fragment, not an answer, and it should never reach a parser or a user as if it were complete. You can usually continue a truncated text response. Tool calls are the exception: if the last content block is an incomplete tool_use, continuing won't produce usable arguments. Send the request again with a higher max_tokens.
# Check if response was truncated during tool use
if response.stop_reason == "max_tokens":
# Check if the last content block is an incomplete tool_use
last_block = response.content[-1]
if last_block.type == "tool_use":
# Send the request with higher max_tokens
response = client.messages.create(
model="claude-opus-5-5",
max_tokens=4096, # Increased limit
messages=messages,
tools=tools,
)tool_use means Claude is waiting for you. Run the tool and return its result. A server tool call that doesn't have its result block yet completes in a later response, so you don't answer it yourself. pause_turn means a server-tool loop hit its iteration limit. Send the assistant content back as it is, and Claude continues from where it stopped.
Checkpoint 2 of 7· Check yourself
A response stops with stop_reason "max_tokens", and its last content block is a tool_use. What should your code do?
A tool_use block cut off by the token limit has incomplete input. The fix is to retry with more room, not to run a partial call or continue the turn.
“the truncated response contains an incomplete tool use block, you'll need to retry the request with a higher max_tokens value”Source: platform.claude.com
Checkpoint 3 of 7· Exam question
A team builds a pipeline that extracts contact fields (name, email, plan) from support tickets and feeds them directly into a database insert without any try/except around the JSON parsing step. The pipeline occasionally crashes because Claude's response includes explanatory text before the JSON or omits a required field. What is the most effective fix to guarantee schema conformance without relying on prompt wording alone?
Correct answer: B — Configure Structured Outputs with a JSON schema in the API's output format field
- A. Incorrect. Wording instructions can reduce but never guarantee stray text or missing fields; Claude can still deviate under load or ambiguity, so the pipeline remains fragile.
- B. Correct. Structured Outputs constrains generation to a declared JSON schema, guaranteeing valid, schema-conforming JSON in the response text and removing the need for prompt-based hoping.
- C. Incorrect. Truncation from running out of tokens is a separate failure mode from stray prose or omitted fields; more tokens doesn't enforce schema conformance.
- D. Incorrect. Backtick stripping only addresses one formatting quirk and does nothing to guarantee required fields are present or types are correct.
Sources1
3.Refusals arrive as successful responses
Claude Fable 5.1, Fable 5, Opus 5.5, Opus 5 and Sonnet 5.5 include safety classifiers that can decline a request. A decline comes back as an ordinary response, so a pipeline that only catches exceptions will miss it. A loop that reads response.content[0] will crash on an empty list. A refusal looks like this:
"content": [],
"stop_reason": "refusal",
"stop_details": {
"type": "refusal",
"category": "cyber",
"explanation": "This request was declined because it could enable cyber harm."
},Branch on stop_details.category, not on the explanation. The explanation text isn't stable, so show it to people and don't parse it. Both fields can be null when a refusal doesn't map to a named category, and that is a normal, permanent value. A refusal can also come mid-stream, after some output has already arrived. Discard that partial output rather than keeping it. To recover, send the same request to another Claude model. The beta fallbacks="default" setting does this on the server side, using the fallback model recommended for the refusal's category.
Checkpoint 4 of 7· Check yourself
A streamed response shows two paragraphs, then ends with stop_reason "refusal". What should happen to the two paragraphs?
Output from before a mid-stream refusal is incomplete and should be discarded. The recovery path is a retry on a fallback model.
“In either case, treat any partial output as incomplete and discard it.”Source: platform.claude.com
Sources2
4.Guard against empty content
A response can end with end_turn and still contain nothing. [Empty end_turn responses](lesson:4.1) usually appear after tool results, when Claude decides the turn is over. In a validation pipeline, that means end_turn alone isn't a passing check. Put the guard next to the stop-reason branch: if response.stop_reason == "end_turn" and not response.content:. Then handle that case explicitly instead of passing an empty list to a parser.
The recovery is the part people get wrong. Sending the conversation back unchanged gets the same empty answer, because Claude has already decided it is done. First fix the message structure, so there are no text blocks straight after tool results. Only if that doesn't work, add a continuation prompt in a new user message.
Checkpoint 5 of 7· Check yourself
Your guard catches an end_turn response with no content blocks, and the message structure is already correct. What should the retry send?
Resending without changes reproduces the empty turn. A continuation prompt in a new user message is the last-resort fix.
“Don't retry empty responses without modification: Sending the empty response back won't help.”Source: platform.claude.com
Sources1
5.Well-formed output is not correct output
A schema guarantees shape, not truth. Constrained decoding makes total_amount a float. It doesn't make it the total printed on the invoice. Fluent, well-formed text can still be wrong because of how [next-token generation and hallucination](lesson:5.1) work. A confidence: 0.92 field is also model output, constrained to be a number and nothing more. Treat confident, valid output as a claim to check against your sources.
Several grounding and verification techniques turn that check into steps your pipeline can run:
- Quote first: for long documents, have Claude extract word-for-word quotes before doing the task, and base the analysis only on those quotes. - Cite and retract: require a supporting quote for each claim, and remove any claim that has no quote. - Best-of-N: run the same prompt several times and compare the results. Inconsistencies across outputs could indicate hallucinations. - Allow uncertainty: give Claude explicit permission to say it doesn't have enough information, so it doesn't fill the gap. - Restrict knowledge: tell Claude to use only the provided documents and not its general knowledge.
Quotes are what make this something code can verify. If the schema asks for the supporting quote next to each extracted value, your validator can check that the quote really appears in the source document before it accepts the record.
Checkpoint 6 of 7· Match them up
Match each verification technique to what it does
Tap a term, then the definition that fits it.
Each technique ties the output back to evidence. Quotes ground the analysis, citations make each claim auditable, repeated runs expose instability, and restriction keeps answers inside the documents.
“If it can't find a quote, it must retract the claim.”Source: platform.claude.com
Checkpoint 7 of 7· Exam question
A summarization service parses the response text as JSON. For very long transcripts, the JSON string is occasionally cut off mid-object and the parser raises a decode error. Investigating, the engineer finds stop_reason is "max_tokens" on every one of the failing calls. What should the defensive parsing logic do?
Correct answer: A — Catch the decode error, check stop_reason for max_tokens, and treat the response as incomplete
- A. Correct. stop_reason "max_tokens" is the documented signal that the response was cut off before completion; defensive code should branch on it and handle the response as incomplete rather than as malformed JSON.
- B. Incorrect. A lenient parser might mask trailing-comma typos, but it cannot recover data that was never generated because the response was truncated mid-object.
- C. Incorrect. Lowering max_tokens makes truncation more likely, not less, worsening the exact failure being investigated.
- D. Incorrect. The failures are explained by output length, not network transience; blindly resending the same request risks the same truncation again.
Sources3
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.A refusal raises an API error, so try/except around the call is enough to catch it.Why is that wrong?
A refusal is a successful 200 response with stop_reason "refusal" and possibly empty content. Only a stop_reason check catches it.
Covered in Refusals arrive as successful responses
2.Any max_tokens stop can be fixed by asking Claude to continue.Why is that wrong?
When the truncated content ends in an incomplete tool_use block, the request has to be retried with a higher max_tokens to get the full tool call.
Covered in Truncated, tool and paused turns
3.Output that passes schema validation can be trusted as factually correct.Why is that wrong?
Schema compliance only constrains shape. Claude can still produce content that is factually wrong or inconsistent with the context, so claims need grounding and citation checks.
Covered in Well-formed output is not correct output
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“Check this field to decide whether to use the response as-is, continue the conversation, retry, or fall back to another model.”
↩︎ Read stop_reason before the content“Unlike errors, which indicate failures in processing your request, stop_reason tells you why Claude completed its response generation.”
↩︎ Read stop_reason before the content“A server tool call still missing its result block completes in a later response.”
↩︎ Truncated, tool and paused turns“Send the assistant content back to continue.”
↩︎ Truncated, tool and paused turns“if response.stop_reason == "end_turn" and not response.content:”
↩︎ Guard against empty content“Use continuation prompts as a last resort: Only if these fixes don't resolve the issue.”
↩︎ Guard against empty content“the truncated response contains an incomplete tool use block, you'll need to retry the request with a higher max_tokens value”
↩︎ Exam trap 2“the truncated response contains an incomplete tool use block, you'll need to retry the request with a higher max_tokens value”
↩︎ Checkpoint“Don't retry empty responses without modification: Sending the empty response back won't help.”
↩︎ Checkpoint - 2.
“The text is not stable, so display it rather than parse it.”
↩︎ Refusals arrive as successful responses“That null is a normal, permanent value, not a placeholder.”
↩︎ Refusals arrive as successful responses“When that happens, you receive a normal response, not an error, with stop_reason: "refusal".”
↩︎ Exam trap 1“A refusal is a successful HTTP 200 response with stop_reason: "refusal"”
↩︎ Prediction“In either case, treat any partial output as incomplete and discard it.”
↩︎ Checkpoint - 3.https://platform.claude.com/docs/en/test-and-evaluate/strengthen-guardrails/reduce-hallucinationsOfficial docs
“ask Claude to extract word-for-word quotes first before performing its task”
↩︎ Well-formed output is not correct output“Inconsistencies across outputs could indicate hallucinations.”
↩︎ Well-formed output is not correct output“Explicitly instruct Claude to only use information from provided documents and not its general knowledge.”
↩︎ Well-formed output is not correct output“can sometimes generate text that is factually incorrect or inconsistent with the given context”
↩︎ Exam trap 3“If it can't find a quote, it must retract the claim.”
↩︎ Checkpoint