AnthropicClaude Certified Architect – Foundations2026-104.3 Enforce structured output using tool use and JSON schemas
Route structured failures by their cause
Completed by the knowledge check
Learning objective
Handle residual structured-output failures: unsupported schema features, refusals, max_tokens truncation, and an absent tool call under auto.
Core idea
Structured output changes which failures you should expect; it does not remove outcome handling. A rejected schema never produces a record. A refusal can arrive as HTTP 200. A token limit can leave an incomplete response. Under tool_choice: {"type": "auto"}, a strict extraction tool can remain unused.
For a document-extraction service, classify the failure before choosing recovery. Changing max_tokens addresses a truncated generation. It does not repair an unsupported schema or make auto compulsory. A stronger prompt can influence a tool choice, but it does not replace supported forcing or a native JSON contract.
See 4.3.2 for choosing native JSON or strict tool use and 4.3.3 for document routing. This lesson owns the residual failure paths after that choice. A consumer should accept only a complete result on the intended output surface, then apply the semantic checks in 4.3.6.
How it works
Repair an unsupported request
JSON outputs and strict tool use share a supported JSON Schema subset. Anthropic documents a 400 error when a schema uses unsupported features. Examples include numeric constraints such as minimum, maximum, and multipleOf; recursive schemas; external $ref references; and string minLength or maxLength. Retrying the unchanged schema does not remove the rejected feature.
Inspect the error and simplify the schema or use a documented SDK transformation. In Python, transform_schema() from the anthropic package returns a transformed schema without sending a request. The Python client.messages.parse() helper transforms a Pydantic model’s schema, sends it as output_config.format, validates the response, and returns parsed_output.
Transformation can strip an unsupported bound and append it to the field’s description. That changes enforcement: the API receives a simpler constraint, and local validation must still check the original bound. Not every helper performs that check. The TypeScript jsonSchemaOutputFormat() helper transforms a raw schema by default but does not validate the response against stripped constraints. Do not treat transformation alone as proof that the full original contract holds.
The request combination also matters. Enabling citations with output_config.format returns 400 because citation blocks require interleaving with text. Choose a compatible output design rather than retrying that combination.
Handle refusal before parsing a record
With structured outputs, stop_reason: "refusal" can accompany HTTP 200, and refusal takes precedence over schema constraints. Check stop_reason before treating the content as the requested JSON or extraction input. A refusal is a response outcome, not evidence that a JSON parser is defective.
Anthropic documents stop_details.category and stop_details.explanation for refusal diagnostics. The category and explanation can be null; handle that as a valid diagnostic outcome. The explanation is human-readable text whose wording is not stable. Do not parse it as a machine-readable classification. Discard partial output from a mid-stream refusal.
A schema field or prompt that asks for private thinking can trigger reasoning_extraction. Inspect field names such as reasoning or trace, the system prompt, and tool descriptions. Ask for a short explanation or supporting evidence instead of internal reasoning. Anthropic documents no recommended fallback for this category: change the request rather than repeat it unchanged. Other refusal routing belongs to the broader response handling in 1.1.1; it is separate from schema repair.
Regenerate a truncated record
When stop_reason: "max_tokens" appears, the response reached the request’s token limit. Structured output can be incomplete and fail the schema. For this path, Anthropic recommends retrying with a higher max_tokens value. Its stop-reason documentation gives the same remedy for an incomplete tool_use block.
Do not insert a missing brace, invent absent fields, or execute an incomplete extraction call. Those repairs can produce parseable content without recovering the generated record. Keep the original source and schema for the retry so the extra output budget addresses the actual cause.
A successful retry still needs the semantic checks in 4.3.6. A complete record can contain a wrong total or a date assigned to the wrong field; increasing the output budget repairs incompleteness, not those errors.
Treat an absent call as an explicit outcome
strict: true constrains a tool call’s name and input when the model makes that call. It does not require a call under tool_choice: {"type": "auto"}. If response.content has no matching tool_use block, return the application’s missing-extraction outcome rather than dereference a nonexistent input or parse arbitrary prose as if it were the tool result.
Where supported, tool_choice: {"type": "any"} requires a tool call, while tool_choice: {"type": "tool", "name": "extract_invoice"} requires the named tool. Claude Opus 5.5, Claude Sonnet 5.5, Claude Fable 5.1, and Claude Mythos 5.1 reject any and tool with 400. Manual extended thinking through thinking: {type: "enabled"} also does not support those choices. See 4.3.3 for the routing design; do not turn a missing call into an unsupported forced request.
On a supported native-JSON path, output_config.format makes the response text the contracted data surface. That is an intentional interface choice, distinct from rescuing free-form text returned under auto.
Key distinctions and trade-offs
Choose recovery from the observable cause:
| Observation | Classification | Appropriate next action |
|---|---|---|
| 400 naming an unsupported schema feature | Request contract rejected | Remove or transform the unsupported feature and retain necessary local validation |
HTTP 200 with stop_reason: "refusal" | Refused output | Read refusal diagnostics and handle the cause before parsing |
stop_reason: "max_tokens" | Incomplete generation | Retry with a higher supported max_tokens |
No extraction tool_use under auto | Tool selection did not produce the record | Handle the missing result and choose a supported output guarantee |
A further documented exception concerns string enum and const capitalization. Both native JSON outputs and strict tool use can return a casing variant without an error or special stop_reason. Anthropic recommends case-insensitive comparison and avoiding enum values that differ only by capitalization. If your consumer needs exact spelling, map a case-insensitive match to the declared canonical value; reject anything that is not such a match.
Architecture / decision scenario
An invoice service observes four failures. A raw schema containing minimum returns 400. Another request completes with refusal after asking for a private reasoning trace. A large line-item record stops at max_tokens. A fourth response under auto contains text but no extract_invoice call.
The service repairs the first request’s unsupported constraint and preserves its local bound check. It removes the private-trace requirement from the second request, asking for short supporting evidence instead. It discards the third response and retries the same extraction with a larger supported max_tokens. It reports the fourth as missing extraction, then changes the interface to supported forcing or native JSON according to the deployed model.
Each path produces either an explicit unsuccessful outcome or a complete record ready for semantic checks. The consumer also canonicalizes documented enum casing variants. One generic “retry failed JSON” branch would obscure the four failures and the casing variant, and repeat requests that need a different change.
Common exam traps
- Retrying a 400 with the same unsupported schema.
- Assuming every schema-transforming SDK helper validates the original constraints.
- Treating HTTP 200 as proof that structured extraction succeeded.
- Repairing truncated JSON instead of obtaining the complete generation.
- Expecting
strict: trueto require a call underauto. - Forcing
anyortoolon a model or thinking setting that rejects it. - Assuming a normal stop reason guarantees exact enum capitalization.
Where the exam guide differs
The guide describes “guaranteed schema-compliant structured output” through tool use. Anthropic’s documentation qualifies that guarantee with unsupported schema features, refusals, truncation, and string enum or const casing variants. The guide also presents any and named forcing without the model and manual-thinking restrictions documented by the product. Read an exam item about structural reliability as a comparison of strict schema enforcement with prompt-only formatting; do not infer that every response is an accepted record or that every model accepts forced selection.
What to remember
Inspect request errors, stop_reason, and the expected content blocks before consuming a record. Repair the rejected contract, address refusal causes, increase a truncating budget, or change the selection guarantee according to the failure. Structural success then leads to semantic validation.
Knowledge check
A raw JSON schema sent through output_config.format includes "minimum": 0 on total, and every request returns 400. The consumer must still reject negative totals. What should the team do?
input_schemaminimum from the sent schema and check the bound locallyminimum and rely on a field description to enforce itA large line-item record ends with stop_reason: "max_tokens", and its JSON stops partway through an array. The consumer needs the complete record. What should it do?
max_tokensoutput_config.format to a strict toolmax_tokens . Closing brackets or nulling missing fields produces parseable content that was never generated, and a strict tool call can be cut off by the same budget.An extraction schema includes a required reasoning_trace field described as “your step-by-step thinking before answering”. Responses now arrive with HTTP 200, stop_reason: "refusal", and stop_details.category set to "reasoning_extraction". What should the team change?
max_tokens so the trace fits in the responsestop_details.explanation to find which schema field to dropstop_details.explanation is human-readable text whose wording is not stable.A strict extract_invoice tool runs under tool_choice: {"type": "auto"}. One response has stop_reason: "end_turn" and a text block, but no tool_use block. What should the consumer do with this response?
input from the first content block as usualstrict: true constrains a call’s input when Claude makes the call; under auto it does not require one. The consumer reports missing extraction rather than parsing prose as if it were the record or dereferencing an input that does not exist, then chooses a supported guarantee for the interface, such as forcing where the model allows it or native JSON outputs. Nothing violated the schema, so an unchanged retry addresses no cause.A strict schema’s category enum includes "freight". A strict extraction call completes normally, with no error, but carries category: "Freight". The consumer’s reports need the exact declared spelling. What should it do?
"freight""Freight" exactly as the extraction call returned it"Freight" to the enum as a second accepted memberReferences
- Anthropic, Structured outputs
- Anthropic, Strict tool use
- Anthropic, Define tools
- Anthropic, Stop reasons and fallback
- Anthropic, Refusals and fallback
- Anthropic, Claude Certified Architect – Foundations Exam Guide