What you will be able to do
- Explain why JSON requested only through the prompt breaks downstream parsers, and how constrained decoding prevents it
- Send a JSON schema in output_config.format, directly or through client.messages.parse()
- Enable strict: true on a tool and know which toolsets reject it
- Pick a fallback when structured outputs can't express the schema you need
Key concept
Constrained decoding — Claude's token sampling is limited to output that is valid under a schema you supply. Because the shape is enforced while the output is generated, the result always parses and always has the required fields and types. You don't need to hope the model followed the instructions.
1.Why JSON requested in the prompt breaks pipelines
Whatever consumes Claude's output is usually code: a json.loads(), a database insert, a function that expects passengers to be an integer. Code does not tolerate approximations. Ask for JSON in the prompt alone and most responses will parse. Then one arrives with a trailing sentence, a missing key or a number written as a string, and the job fails at 3 a.m. on a record nobody can reproduce.
Careful prompting lowers how often this happens, but it can't bring the rate to zero. Four kinds of failure keep appearing: parsing errors from invalid JSON syntax, missing required fields, inconsistent data types, and schema violations that you have to catch and retry. A booking tool shows the type problem clearly. It needs passengers: int, and without enforcement Claude might send "two" or "2".
Structured outputs fix this at generation time. Claude's sampling is restricted so that only schema-valid output can be produced. That makes the result always valid (no more JSON.parse() errors), type safe (required fields and their types are guaranteed), and reliable, because there are no schema violations left to retry. The feature has two parts, and you can use them separately or together in one request. JSON outputs shape Claude's response. Strict tool use shapes the arguments Claude passes to your tools.
Checkpoint 1 of 7· Check yourself
A team asks for JSON with a detailed system prompt and a worked example. Their parser still fails now and then. Which explanation fits?
Without constrained decoding, malformed JSON and invalid tool inputs stay possible however good the prompt is. Only structured outputs guarantee a schema-compliant result.
“Without structured outputs, Claude can generate malformed JSON responses or invalid tool inputs that break your applications.”Source: platform.claude.com
2.JSON outputs with output_config.format
There are three steps to a JSON output. Describe the structure you want as a JSON schema, or as a type in your language. Send it in the request's output_config.format with type: "json_schema". Then read the result from the response's text content block. The schema follows JSON Schema, with some limitations. The raw request looks like this:
output_config={
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"},
"plan_interest": {"type": "string"},
"demo_requested": {"type": "boolean"},
},
"required": ["name", "email", "plan_interest", "demo_requested"],
"additionalProperties": False,
},
}
},
)
text = next(block.text for block in response.content if block.type == "text")
contact = json.loads(text)Checkpoint 2 of 7· Put it in order
Put the steps for getting a JSON output in order
- 1.Read the JSON from the response's text content block
- 2.Send the schema in output_config.format with type: "json_schema"
- 3.Define the structure as a JSON schema or as a type in your language
The schema is defined first and carried in output_config.format. Claude then returns matching JSON in a text content block.
“The request carries the schema in output_config.format with type: "json_schema"”Source: platform.claude.com
The recommended route in Python is the SDK helper. Pass a Pydantic model to client.messages.parse() as output_format=. The SDK converts the model to a schema, sends it as output_config.format, validates the response and returns the typed object in parsed_output. output_format is the helper's argument and output_config.format is the request field the helper fills in. They are the same feature at two layers.
Some JSON Schema constraints can't be expressed in the sent schema, and the helpers deal with that without hiding it. Constraints such as minimum, maximum, minLength and maxLength are removed from the schema Claude receives and written into the field's description instead. The helpers also add additionalProperties: false to every object and filter string formats down to the supported list. A helper that validates responses still checks the result against your original schema in your own code. Take a field with minimum: 100: it goes to Claude as a plain integer with {minimum: 100} in its description, and the response is still validated against minimum: 100. If you build the request yourself, transform_schema() from the anthropic package does the same conversion and returns the schema for you to edit instead of sending it.
Checkpoint 3 of 7· Check yourself
Your Pydantic model declares quantity with a minimum of 100, and you call client.messages.parse(). What does Claude's schema contain, and where is the minimum enforced?
Unsupported constraints are moved into the field description, so Claude sees them as guidance. The helper still enforces them on the response in your code.
“Claude receives a simplified schema, but a helper that validates responses still enforces every constraint in your code.”Source: platform.claude.com
Sources1
3.Strict tool use: guaranteed tool inputs
Agents fail in a different place. The risk is not the final answer but the arguments Claude passes to your functions. If a create_booking handler gets "2" where it expected 2, or gets no value for a required field, it throws at runtime partway through a multistep task. Setting strict: true on a tool definition compiles its input_schema into a grammar, using the same pipeline as structured outputs, and limits Claude's sampling to inputs that match it.
strict is a top-level property of the tool, next to name, description and input_schema. With it on, you get two guarantees. The input in every tool_use block follows your input_schema, and the tool name is always valid: one of the tools you provided, or a server tool. Your handler can drop the validate-and-retry loop for tool arguments.
Checkpoint 4 of 7· Fill the gap
Which key turns on schema-guaranteed inputs for this tool?
"name": "get_weather",
"description": "Get the current weather in a given location",
" ? ": True, # Enable strict modestrict: true, set at the top level of the tool definition, enables grammar-constrained sampling of the tool input. required and additionalProperties belong inside the input_schema and don't turn on enforcement.
Strict mode has three operational limits. The computer use and browser use toolset entries (computer_toolset_20260801 and browser_toolset_20260801) don't accept strict: true, and a request that sets it on either one is rejected. Compiled tool schemas are cached for up to 24 hours after last use, apart from message content. Strict tool use is HIPAA eligible, but that cache doesn't get the same PHI protections as prompts and responses. Keep PHI out of property names, enum values, const values and pattern regexes.
| Feature | Where you set it | What it constrains | Where the result arrives |
|---|---|---|---|
| JSON outputs | output_config.format with type: "json_schema" | Claude's response | The response's text content block |
| Strict tool use | strict: true on the tool definition | Tool names and tool inputs | The input field of the tool_use block |
Checkpoint 5 of 7· Check yourself
An agent defines a custom search_flights tool and the computer_toolset_20260801 entry, and sets strict: true on both. What happens?
The computer and browser use toolset entries reject strict: true, and the whole request fails. Set strict only on your own tool definitions.
“The computer use and browser use toolset entries (computer_toolset_20260801 and browser_toolset_20260801) don't accept strict: true”Source: platform.claude.com
Sources2
4.When structured outputs can't be used
Before giving up on constrained decoding, check whether the obstacle is just an unsupported constraint. A minimum, a maximum or a string length doesn't block structured outputs. transform_schema() or a validating SDK helper moves it into the description and checks it in your code. The cases that do force a fallback are a model where structured outputs aren't available and a schema that needs features the supported subset doesn't cover.
For those cases, you go back to asking for the format in the prompt. The techniques are taught in [Prompting for output format](lesson:6.2). In short: define the format precisely with JSON, XML or a template, prefill the assistant turn, and constrain with examples. What changes in your pipeline is that the output no longer comes with a guarantee. Every failure mode constrained decoding removed is possible again: syntax errors, missing fields, wrong types. Wrap the parse in error handling, validate against the schema in code, and budget for retries. You already had these steps before structured outputs existed.
Checkpoint 6 of 7· Check yourself
A feature runs on a model without structured outputs, so the team switches to a precise JSON template plus an example in the prompt. What should change in the code that consumes the output?
Prompting improves consistency but enforces nothing, so the errors that structured outputs had removed must be handled in code again.
“Schema violations requiring error handling and retries”Source: platform.claude.com
Checkpoint 7 of 7· Exam question
A weather tool occasionally fails because an upstream API times out. The integration currently returns a bare tool_result of "error" with is_error: true, and Claude keeps repeating the same failing call in a loop. What change to the tool_result content would most likely stop the repeated failing calls?
Correct answer: A — Replace the generic "error" text with a specific message describing the failure and a retry delay
- A. Correct. Instructive error messages that name the failure and suggest a concrete next step give Claude the context needed to adapt, such as waiting before retrying, instead of repeating the identical call.
- B. Incorrect. Hiding the error would cause Claude to treat failed data as valid, corrupting downstream output rather than stopping the loop.
- C. Incorrect. Omitting a required tool_result block after a tool_use block violates the API's formatting requirements and produces a request error, not a resolved loop.
- D. Incorrect. Converting a custom client tool into a server tool isn't a configuration option available to arbitrary user-defined tools, and it wouldn't address the message-quality problem.
Exam traps
Each one states something that sounds right. Open it to see what is actually true.
1.A clear enough prompt with an example makes Claude's JSON reliable enough for an unguarded parser.Why is that wrong?
Prompting only improves the odds. Malformed JSON, missing fields and wrong types are ruled out only when constrained decoding enforces the schema.
Covered in Why JSON requested in the prompt breaks pipelines
2.strict: true can be set on every entry in the tools array, including the computer and browser use toolsets.Why is that wrong?
Those two toolset entries reject strict: true, and a request that sets it on either one fails.
Covered in Strict tool use: guaranteed tool inputs
Sources
Every claim above is drawn from one of these pages, quoted as it was written on the date shown.
- 1.
“Without structured outputs, Claude can generate malformed JSON responses or invalid tool inputs that break your applications.”
↩︎ Why JSON requested in the prompt breaks pipelines“You can use these features independently or together in the same request.”
↩︎ Why JSON requested in the prompt breaks pipelines“The SDK transforms the model's schema, sends it as output_config.format, validates the response, and returns the parsed model in parsed_output.”
↩︎ JSON outputs with output_config.format“A field with minimum: 100 becomes a plain integer in the sent schema”
↩︎ JSON outputs with output_config.format“pass the schema through transform_schema() from the anthropic package before sending it”
↩︎ When structured outputs can't be used“Structured outputs guarantee schema-compliant responses through constrained decoding”
↩︎ Key concept“Even with careful prompting, you may encounter:”
↩︎ Exam trap 1“The request carries the schema in output_config.format with type: "json_schema"”
↩︎ Checkpoint“Claude receives a simplified schema, but a helper that validates responses still enforces every constraint in your code.”
↩︎ Checkpoint“Schema violations requiring error handling and retries”
↩︎ Checkpoint - 2.
“Without strict mode, Claude might provide passengers: "two" or passengers: "2".”
↩︎ Why JSON requested in the prompt breaks pipelines“Setting strict: true on a tool definition guarantees Claude's tool inputs match your JSON Schema”
↩︎ Strict tool use: guaranteed tool inputs“Tool name is always valid (from provided tools or server tools)”
↩︎ Strict tool use: guaranteed tool inputs“Do not include PHI in input_schema property names, enum values, const values, or pattern regular expressions.”
↩︎ Strict tool use: guaranteed tool inputs“a request that sets it on either entry is rejected”
↩︎ Exam trap 2“The computer use and browser use toolset entries (computer_toolset_20260801 and browser_toolset_20260801) don't accept strict: true”
↩︎ Checkpoint - 3.https://platform.claude.com/docs/en/test-and-evaluate/strengthen-guardrails/increase-consistencyOfficial docs
“Precisely define your desired output format using JSON, XML, or custom templates”
↩︎ When structured outputs can't be used