Structured Output and Why It Matters
A schema gives software something it can check. What each layer of checking actually establishes, and what valid output still does not tell you.
A model can return a sentence, and a person can read it. Software cannot, reliably: the phrasing changes between runs, and code that worked on yesterday's wording breaks on today's.
Asking for a defined shape fixes that particular problem. It is worth being precise about which problem it fixes, because a schema is often treated as if it made the whole response trustworthy, and it does not.
What a schema changes
Say you need an invoice amount out of an email. Ask in prose and you get a sentence. Ask for a shape and you get something like this:
{"invoice_id": "INV-4021", "amount": 100, "currency": "GBP"}
Now software can read the amount without parsing English. More importantly, if the model returns something else, code can reject it. That is the real shift. The requirement moved out of the instruction, where nothing was watching, and into a validator, where something is.
Ask in prose
- A sentence comes back
- The phrasing changes between runs
- Nothing checks it
Ask for a shape
- invoice_id, amount, currency
- Software reads the amount without parsing English
- Code can reject anything else
The machine-enforceable contract begins where software can validate explicit rules.
This is the boundary the prompting page stops at. The machine-enforceable contract begins where software can validate explicit rules.
Each check establishes less than you think
There are five distinct things people run together. They are not the same, and passing one says nothing about the next.
Parse. Can the response be read at all? A JSON parser answers this. It is a yes or no about syntax.
Schema validation. Are the required fields present, with allowed types and values? This is where a declared contract does its work: required keys, types, enums, ranges. Still purely about shape.
Meaning. Do the values make sense for this task? Some of this is machine-checkable against rules you can write down: an end date must fall after a start date, a quantity must be positive, an identifier must exist in a catalogue you control. These are real checks, and they are not syntax.
Authorization. Is this operation permitted, for this user, right now? Nothing above answers that. It belongs to a different layer, and it gets its proper treatment in the pages on governed execution.
Execution. Did anything actually happen? A well-formed, schema-valid, semantically sensible, fully authorised instruction can still fail when it runs.
Put together:
Valid structure does not mean correct meaning, factual truth, permission to act, or successful execution.
Parse
Can the response be read at all?
Schema validation
Are the required fields present, with allowed types and values?
Meaning
Do the values make sense for this task?
Authorization
Is this operation permitted, for this user, right now?
Execution
Did anything actually happen?
Here is the compressed version of the same point. Both of these are valid JSON:
{"invoice_id": "INV-EXAMPLE", "amount": "one hundred"}
{"invoice_id": "INV-EXAMPLE", "amount": 100}
Only the second satisfies a contract requiring a numeric amount. Neither establishes that the invoice exists, that 100 is the right figure, or that paying it is allowed.
Where checking stops being possible
Machine-checkable meaning constraints are genuinely useful, and they have a ceiling. A validator can confirm that an identifier appears in your catalogue. It cannot confirm that it is the identifier the customer meant. It can confirm the amount is numeric and within a range. It cannot confirm the invoice is real.
Validation can reject what it can recognise. Anything outside the rules you wrote passes through untouched. Silence from a validator is not approval. It means no rule applied.
- The rules you wrote
What it can recognise
- The identifier appears in your catalogue
- The amount is numeric and within a range
What passes untouched
- Whether it is the identifier the customer meant
- Whether the invoice is real
- Anything outside the rules
- The rules you wrote
Silence from a validator is not approval. It means no rule applied.
The model stays probabilistic
One thing that gets muddled. A validator can be deterministic while the generation it checks is not. Given the same candidate and the same rules, the validator returns the same verdict every time. That is a property of the validator.
It does not make the model deterministic. A schema does not stop the model varying; it gives you a dependable answer about whether a particular output is acceptable. The variation is still there. You have just stopped being surprised by it downstream.
When validation fails
Rejecting bad output is only half a system. The other half is what happens next.
A bounded repair loop is the common pattern: take the exact validation error, hand it back, and ask for a corrected response.
candidate
→ validate
→ return the exact validation error
→ request a correction
→ validate again
The detail that makes it work is the word exact. A generic "that was wrong" is much weaker than naming the field, the rule, and the value that failed.
Two cautions. This is a useful mechanism, not a guarantee, and some candidates will fail again. And the loop needs a limit set by your application, because "retry until it validates" is not a policy. Decide the ceiling, and decide what happens when it is reached: fall back, stop, or send it to a person.
A candidate comes back
Validate it
Return the exact error
The field, the rule, and the value that failed.
Request a correction, and validate again
At the limit you set: fall back, stop, or send it to a person
Failure rates tell you something narrower than you want
Counting rejections is worth doing. A sudden rise usually means something changed: a model update, a prompt edit, a new kind of input.
Be careful what you read into it. Validation telemetry describes validator outcomes. It tells you how often output failed the rules you wrote. It does not by itself measure model quality, factual accuracy, or whether users got what they needed, because it can only see the rules it has. A rise is a signal worth chasing. A flat line is not proof that anything is right.
A rise in rejections
- Something probably changed
- A model update, a prompt edit, a new kind of input
What it does not measure
- Model quality
- Factual accuracy
- Whether users got what they needed
It can only see the rules it has.
Version the schema with the prompt
Once more than one thing depends on a response format, changing a field name quietly breaks something. Treat the schema the way you would treat any interface others depend on: add fields compatibly, deprecate deliberately, and change the instruction and the schema in the same commit, because they are one contract split across two files.
From meaning to action
This page stops at the point where the output is well-formed and the values make sense. Whether the action is allowed, what runs it, and how you know it worked are separate questions with their own boundaries.
To draft a small schema from a list of required fields, the prompt-to-json tool will do it. It drafts a schema and a sample instance; it does not establish that the sample is correct, which is the distinction this whole page is about.
Where this helps, and where it stops
- What it is
- A contract for the shape of data exchanged between a model and software, and an account of what each check establishes.
- What it does not guarantee
- Proof of factual accuracy, authorization, or successful execution, and not a guide to any one provider's structured-output feature.
- When the distinction matters
- A model response can update records, send messages, or trigger other actions.