AI agents

When a Model Ignores the Schema: Diagnostic Playbook

Antonio Evans9 min read

A split scene: on the left, a developer looks at a monitor showing a JSON schema with a red X over a missing field; on the right, the same developer holds a decision tree printout with branches labeled 'missing field'.
Key points

Key points

  1. 1Most schema failures trace to a mismatch between what the prompt asked for and what the validator enforces, not to a model that cannot produce JSON.
  2. 2Replaying the exact failing input with the exact prompt and schema is the cheapest first step and often reproduces the cause in one call.
  3. 3Schema-constrained decoding guarantees syntax but not value correctness, so semantic checks still belong in your code.
  4. 4Rank fixes by cost: tighten the validator, then tighten the prompt, then simplify the schema, then change the model.

Start from the failing field, not the failing run

A schema failure looks like one incident in a dashboard and like one very specific thing in a trace. Before you touch the prompt, open the raw response and find the field the validator complained about. Then answer four questions in order: is the field missing, misnamed, mistyped, or present but wrong? Each answer points at a different layer and a different fix.

This is worth doing because structural fidelity is an operational prerequisite, not a cosmetic one. A semantically correct output that violates the expected contract is, from the consuming system's perspective, indistinguishable from a wrong answer the empirical study of structured output control. Your parser does not know the model meant well.

The four-question triage also separates cheap fixes from expensive ones. A missing field is usually a prompt problem. A misnamed field is usually a contract problem. A mistyped field is usually a schema ambiguity problem. A present-but-wrong field is often not a schema problem at all, and rewriting your schema will not fix it.

The decision tree

What you see in the traceLikely causeFirst checkOne-line fixRelative cost
Required key absentPrompt does not make the field salient, or the model judged it "obvious"Diff prompt's field list against schema required arrayCopy the schema's required array verbatim into the prompt and add one filled exampleLow
Key renamed (current_state instead of status)Model inventing a label, or prompt and schema using different namesGrep the prompt for the exact key spellingUse the schema's exact property names in the prompt and in every exampleLow
Wrong type (integer id, string date)Type not stated, or stated weaklyCheck whether the schema declares type and whether the prompt repeats itAdd explicit type and format rules; prefer a format or pattern over proseLow
Value outside allowed setEnum missing or too openRead the schema's enum listReplace free text with an explicit enum and list every allowed valueLow
Everything present, values wrongData or reasoning problem, not a schema problemCompare the value against the source textFix retrieval or give the model the source spanHigh
Nested object collapses to a stringDepth beyond what the model reliably emitsCount nesting levels in the schemaFlatten one level or split into two callsMedium
Intermittent, input-dependentMarginal capability for this schema complexityStratify failures by input length and schema sizeReduce schema complexity or move to a stronger modelHigh

Read the table left to right. The cost column is what you pay in engineering time and blast radius, not in tokens. A validator change is a five-minute edit; a model change is a migration with evaluation runs.

First: replay the exact failing input

Before theorizing, replay. Take the failing request from your logs, including the full prompt, the schema you actually passed, and the model's raw output. Send it again with temperature near zero so you get a reproducible result, and keep the raw text of every response, since most "not valid JSON" reports come from stray commentary wrapped around the object the MLflow guide to structured outputs.

Then do one thing that surprises most teams: parse the raw output yourself with the same validator, in a scratch shell. You want to know whether the model produced something the validator rejected, or something your pipeline mangled before the validator saw it. Markdown fences, leading prose, and trailing explanations are all classic parse failures, and they are not model failures at all.

A minimal replay loop looks like this:

raw = call_model(prompt=prompt, schema=schema, temperature=0.0)

with open("trace_raw.txt", "w") as f:
    f.write(raw)                 # keep the bytes the model actually sent

parsed = json.loads(strip_fences(raw))   # your extraction layer, not the model
errors = validator.iter_errors(parsed)
for e in errors:
    print(e.json_path, e.message)        # fails at the exact field

If strip_fences is doing heavy lifting, that is already your answer. Fix the extraction layer first, because a validator that never sees valid JSON will keep generating false alarms.

Second: check whether the validator is stricter than the schema the model saw

This is the most common and least discussed cause. The model was given a schema, but the validator enforcing it applies extra rules: additionalProperties: false where the prompt allowed extra keys, stricter formats, a required array the prompt never mentioned, or a newer schema dialect whose keywords the validator interprets differently the JSON Schema validation guide.

Two concrete traps. First, defining a property under properties does not make it mandatory; only the explicit required array does, and a model reading a schema can miss that distinction just as a human reviewer can same guide. Second, dialect drift: JSON Schema is a family of drafts, and keywords can differ by dialect, so the schema's $schema declaration and the validator's configuration must agree same guide.

The decision rule: if the failing field appears in the schema the model saw but in a weaker form than the validator enforces, you have a validator problem. Fix the validator or regenerate the schema the prompt embeds from the authoritative validator config. Do not "tell the model to try harder" for a rule it was never given.

Test the schema itself, not just the responses

Schemas are production code and deserve their own tests. Feed each schema a known-good fixture and a known-bad fixture, and confirm the validator accepts and rejects them respectively. Teams that do this catch the validator-drift bug before it becomes an incident, and it is the same discipline as validating third-party responses in place rather than only in mocks validating third-party API responses with JSON Schema.

Third: is the prompt making the field obvious?

If the schema and validator agree, the model was told what to do and did not do it. The usual cause is salience. A schema embedded as a dense JSON blob, with the critical field buried three levels deep, competes with everything else in the context window. Prompted JSON gives no guarantee, which is exactly why schema-constrained generation and function calling became the default for production pipelines the MLflow guide.

Cheap prompt fixes, in order:

  1. Restate the required array as a plain sentence above the schema, for example "Every response must include id, status, currency, total, and items."
  2. Use the schema's exact property names everywhere, including in examples. Renamed fields are almost always a case where the prose used one word and the schema used another.
  3. Add one filled example of the exact output shape. One in-context example does more work than a paragraph of instructions, and practitioners report valid-JSON rates moving from roughly 60 percent to above 95 percent after adding schema instructions and strict field rules, on their own measurement the structured output prompting guide.
  4. Tell the model what not to do: no markdown fences, no commentary before or after the object. Parsers handle fewer surprises when the prompt names the failure mode the MLflow guide.

If none of this moves the failure rate, stop editing prose. You are at the capability boundary.

Fourth: simplify the schema

When a model follows simple schemas and misses complex ones, the fix is schema design, not a longer prompt. Three levers, in order of payoff.

Flatten nesting. Every level of nesting is another place to lose the thread. Splitting a deeply nested extraction into two calls, one per object, is often cheaper than fighting the model.

Replace open strings with explicit enums. A status field that allows any string invites paraphrasing; an enum of pending, paid, cancelled does not. Keep schemas minimal with required fields, strict types, and enums to prevent ambiguous outputs the MLflow guide.

Remove optional fields you do not consume. Every optional property is a chance for the model to include it when it should not, or format it in a way your code does not expect.

There is a ceiling here, and it is worth naming. A template-driven control that nearly eliminates syntax errors still leaves substantial structural and semantic errors, which shows the bottleneck sits beyond formatting the empirical study of structured output control. Simplifying the schema buys reliability, but it does not buy correctness of values.

Fifth: change the enforcement mechanism or the model

If you are still on prompted JSON, move to schema-constrained generation or function calling. Constrained decoding guarantees syntactically valid output before a token is spent on something a parser will reject, and it reduces parsing errors better than prompted JSON, though it does not ensure value correctness the MLflow guide. This is the single highest-leverage change for teams still hand-parsing.

Before you swap models, stratify. Group failures by schema complexity and input length. A model that fails only on the most complex schema in your fleet is a schema problem wearing a model costume. A model that fails across simple and complex schemas alike is a capability problem. When you do need to compare candidates, compare models under the same extraction frame: API-native structured outputs and function calling are built-in provider features that enforce strict formats, while library approaches work across models but stay fragile the structured outputs and function calling guide. Comparing a constrained-output model against a prompted-output model tells you nothing useful.

If your failure rate is split across several providers, routing all of them through one endpoint removes a variable from the comparison. Quanta Inference puts text, image, video, audio and embedding models behind one OpenAI-compatible API key, which makes an A/B run against the same schema a config change rather than an integration project: https://quantaintelligence.ai/inference

Build the retry path with intent

A schema failure is a validation failure, and validation failures are the category where the orchestrator cannot repair the payload itself. The model must read the error, adjust, and produce a corrected request the LLM tool calling error handling guide. That distinction drives your retry policy: do not retry a schema rejection blindly, and do not route it to the same circuit-breaker path as a 503.

Two rules do most of the work.

First, feed the error back in structured form. An LLM given "invalid date format, expected ISO 8601 (YYYY-MM-DD)" can self-correct on the next attempt; an LLM given a raw stack trace usually cannot error handling and recovery patterns for agents. Include the failing JSON path, the expected type, and the observed value.

Second, classify the error before you retry it. Transient infrastructure errors deserve exponential backoff with jitter; permanent errors such as invalid input or a malformed request body will fail on every attempt and should fail fast AWS retry behavior, the Temporal non-retryable errors pattern. Cap retries at three to five attempts; if it has not worked in three, it will not work in thirty AI agent error handling that works.

One caution: retrying a run that already committed a side effect is a different problem. If the schema failure happens after a tool call fired, a retry can duplicate the write. Idempotency keys generated by the orchestration layer, not by the model, make the retry safe agent tool call retries and idempotency, 7 patterns that make agent retries idempotent.

Instrument the failure so it does not come back

Log both the raw and parsed output for every call, and track a validation failure rate as a first-class metric the MLflow guide. A method that works one day and fails after a model change is a regression you can only see if you measured it.

Keep a small regression suite of the inputs that once failed, each with its exact prompt and schema. A passing fixture suite of easy cases hides real-world misses precisely because the fixtures were generated from the same schema the model finds easy. The fixtures that matter are the ones that broke.

Watch latency and timeouts alongside correctness. A call that returns malformed JSON after a 30-second wait costs you twice, and recommended timeouts vary by operation type rather than being one global number commercetools error handling.

If the failures cluster in one line of business rather than one endpoint, the schema is a symptom of a workflow nobody has modeled end to end. That is when it helps to have AI experts read your context and hand back finished deliverables rather than another prompt to tune: https://quantaintelligence.ai/agents

FAQ

FAQ

Why does my fixture suite pass while production misses the schema?

Fixtures are usually generated from the same schema the model finds easy, so they test the happy path. Production inputs are longer, messier, and more nested, and models fail structurally more often on unfamiliar shapes. Keep a regression set built from real failing inputs, and test the schema itself with known-bad fixtures.

Should I change the model or the prompt first?

Neither, until you have replayed the failing input and compared the schema the model saw against the validator's rules. If those disagree, you have a validator problem and no model change will help. If they agree, try the cheap prompt fixes and one filled example before considering a model swap.

Schema-constrained generation guarantees valid JSON, so why do I still get bad values?

Constrained decoding enforces syntax, not meaning. It will not stop a model from putting a plausible but wrong number in a typed field. Keep semantic checks in your code, and treat the schema as a structural boundary rather than a correctness guarantee.

How many times should I retry a schema rejection?

Treat a validation rejection as terminal for that attempt and route it to a correction path where the structured error goes back to the model, then allow at most three to five total attempts. Retrying the identical payload produces the identical rejection, and it burns budget that other steps in the run need.

What is the fastest way to compare two models on the same schema?

Hold the extraction frame constant: same prompt, same schema, same validator, same regression set, temperature near zero. Compare failure rates by schema complexity and input length rather than a single average. Running both models through one compatible endpoint removes integration differences from the comparison and keeps the result honest.

Sources

Sources

  1. Empirical Study for Structured Output Control in LLMs for ...
  2. Enforce, Validate, Observe: 3 LLM Structured Output ...
  3. API Testing JSON Schema Validation Guide
  4. using JSON Schema for 3rd party API response validation
  5. Structured Output JSON: Prompts With Schema Validation
  6. The guide to structured outputs and function calling with ...
  7. LLM Tool Calling Error Handling: Retries and Fallbacks
  8. Error Handling and Recovery Patterns for AI Agents
  9. Retry behavior - AWS SDKs and Tools
  10. Non-Retryable Errors
  11. Patterns AI Agent Error Handling That Actually Works
  12. Agent Tool Call Retries: Idempotency and Recovery
  13. 7 patterns that make agent retries idempotent, not duplicative
  14. Error handling through timeout and retries | API
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles