Structured JSON Output From LLMs: How to Avoid Broken Workflows

Structured JSON Output From LLMs: How to Avoid Broken Workflows

Get structured JSON output from LLMs that other systems can use: combine a clear schema, code-side validation and capped retries so nothing breaks downstream.

••

Want to apply this in your company?

Book a call with Juan →

Structured JSON Output From LLMs: How to Avoid Broken Workflows

To get reliable structured JSON output from an LLM, combine three layers: a clear schema the model must follow, validation in your own code before any system consumes the result, and a retry policy with a hard limit and a fallback. No single layer is enough on its own. Asking for JSON in the prompt often works in testing and fails on the odd case, and that odd case is where the workflow breaks.

Key takeaways

  • Valid JSON is not the same as correct output: the text can have perfect syntax and wrong content.
  • Constraining output with a schema reduces format failures, but validation in your own code is still mandatory.
  • Small schemas with explicit allowed values and a way to express uncertainty break less often than large, open-ended ones.
  • Retries only make sense for failures that could change on a second call, with a fixed limit and a final destination for cases that never pass.
  • Start with one real workflow, log every failure, and agree on a success criterion before you expand.

Why does model output break workflows?

An LLM generates probable text, not guaranteed objects. When another system expects an object with defined fields and types (a CRM, an ERP, a queue, a spreadsheet), any deviation turns into a chain of errors. These are the deviations most commonly described and most often seen in implementation testing:

  • Extra text around the JSON, such as an introductory sentence or a formatting fence.
  • A response cut off midway because the output token limit was reached.
  • A required field that is missing or named slightly differently from what was agreed.
  • The wrong type, such as a number delivered as text or a date in an unexpected format.
  • A value outside the allowed list, for example a category that does not exist in your records.
  • A well-formatted but invented value: a phone number, an order number or a date that the source text never contained.
  • A change in behavior after switching model versions or editing the prompt.

The first five are format failures and can be handled with a schema and validation. The last two need more: checks against the source and regression tests. It helps to keep the two groups apart, because the defenses are different.

Schema, JSON mode or prompt: what does each layer guarantee?

To separate official documentation from implementation experience: model providers usually offer more than one mechanism, under different names. Always confirm in your provider's documentation what is available and what the limitations are, because they change.

  • Asking in the prompt: you describe the format and include examples. This is the most flexible option and the least guaranteed. It suits prototypes and works as a complement to the other layers.
  • Provider JSON mode: generally raises the chance that the response is syntactically valid JSON, but does not guarantee that fields and types follow your schema.
  • Schema-constrained output (often called structured outputs) or a tool call with typed parameters: the provider restricts generation to a schema you supply. It usually supports only part of the JSON Schema specification, so very elaborate schemas may be rejected or simplified. To understand how the model fills in tool arguments, see function calling explained.
  • Validation in your code: runs after any of the three above. It is the only layer you fully control and the only one that knows your business rules.

Even with schema constraints, treat a response cut off by the token limit and a model refusal as possible outcomes that may not deliver the complete object (confirm the exact behavior in your provider's documentation). That is why validation is not redundant.

A quick decision guide:

  • If the result is only read by people, a prompt with examples is enough.
  • If the result feeds another system, use schema-constrained output or a typed tool when the provider offers it, and always validate.
  • If your provider or chosen model does not offer schema constraints, use JSON mode if it exists, plus strict validation and retries that send the error back to the model.
  • If the result triggers an action with external effects (billing, cancellation, sending a message), add human approval or deterministic rules before execution.

How do you design a schema the model gets wrong less often?

The schema is part of the prompt. A poor design increases failures even when constraints are active. These practices tend to help (implementation experience, to be validated in your own case):

  • Few fields. Ask only for what the next system actually consumes. This also supports the data minimization principle in Brazil's LGPD: if the system does not need a national ID number, do not ask the model to return one.
  • Explicit allowed values. Use closed lists (enums) for categories, statuses and priorities, and keep the list aligned with your real records.
  • A way out for uncertainty. Allow a null value or an option such as not identified. Without it, the model tends to fill in something plausible just to satisfy the format.
  • Clear names and descriptions. A field called customer_preferred_date with a short description gets filled in more accurately than a field called date.
  • Shallow nesting. Deep structures and free-length lists increase the chance of truncation and type errors.
  • Fixed formats for dates, currency and phone numbers, described in the schema and normalized afterward by your code.
  • Schema versioning. A version field or file name lets you know which contract each record followed.
  • Separate extracting from deciding. One step extracts facts from the text; another, deterministic or reviewed, decides what to do with them.

The natural-language instruction matters too. To see how to write instructions that support this kind of contract, read how to write instructions for an AI support agent. Anthropic's engineering guide on writing effective tools for agents covers the same principle: clear names, descriptions and parameters reduce usage errors.

What should you validate beyond the format?

Format validation answers whether the object has the expected shape. What actually protects the workflow are the layers after it, in order:

  • Syntax: is the text complete, parseable JSON?
  • Schema: are required fields present, types correct and values inside the allowed list?
  • Business rules: is the date in the future when it should be? Is the amount within an acceptable range? Does the identifier exist in your records?
  • Consistency with the source: does the extracted data really appear in the message, document or record it came from? An order number that is not in the text is a sign of invention.
  • Action permission: even if everything is correct, may this workflow perform this action in this context?

Anything that fails at any layer does not move forward. It goes to a human review queue, with the reason recorded. This logic of checking input, output and action is detailed in guardrails for AI agents.

There is also a security point: model output is untrusted data. OWASP treats improper output handling as a specific risk in its Top 10 for LLM applications. In practice, this means never building a database query, a command or HTML by directly concatenating fields returned by the model. Use parameterized queries and proper escaping, as you would with any user input.

When should you retry and when should you stop?

A retry is useful when the second call has a real chance of producing a different result. It is not useful when the problem lies in the request, the schema or the data. A simple decision rule:

  • Truncated response: retry with more output room or a leaner request, and review the schema if it happens often.
  • Invalid JSON or missing field: resend the same task including the validation error message, short and specific, so the model can correct itself.
  • Value outside the allowed list: a retry with the specific error may work; if it repeats, the list or its description is ambiguous.
  • Business rule violation: do not repeat blindly. It may be bad data at the source. Send it to review.
  • Rate limit error or provider outage: this is an infrastructure problem, not a content problem. Use progressive backoff and a queue, as described in handling LLM API rate limits and outages.

A few precautions prevent damage when repeating:

  • A fixed retry limit, for example two or three, defined per workflow.
  • A clear final destination: after the limit, the case goes to a person or to a visible error state, and is never silently discarded.
  • Idempotency in actions. If a repeated attempt triggers the same effect twice (two charges, two messages), the problem is worse than the original failure. Stripe's documentation on idempotent requests explains the idempotency key concept well and is a useful reference even outside payments.
  • A record of every attempt: input, schema, model version, raw output, validation result and failure reason. Without it there is no way to improve. Be sure to mask personal data in these logs.

Step by step to production without surprises

The method we apply in this kind of work follows four moves: map, limit, pilot and hand over. Applied to structured output, it looks like this:

  • Map: describe the current workflow and identify who consumes the model's output (a system, a spreadsheet, a person) and which fields that consumer really uses. Measure how the work is done today so you have a baseline. The step-by-step is in how to map business processes before applying AI.
  • Limit: reduce the schema to the minimum necessary, close the value lists, and define which actions the workflow may take on its own and which require approval.
  • Build a set of test cases with common inputs, bad inputs and ambiguous inputs, each with its expected result. OpenAI's documentation on evals describes the general logic of testing model outputs systematically.
  • Pilot: run a single real workflow, log failures by type (syntax, schema, rule, source) and agree on a success criterion before you start. To choose metrics and decision points, see how to measure an AI automation pilot.
  • Turn on regression testing: every change to the prompt, schema or model reruns the test set before going live.
  • Hand over: deliver the versioned schema, the tests, the failure dashboard and the procedure for the review queue. Then decide whether to expand, adjust or stop based on the logs, not on impressions.

Illustrative scenario: a clinic receives rescheduling requests by message and wants the model to return an object with the request type, preferred date and a patient identifier for the scheduling system. The schema accepts only three request types, allows a null date and asks for no clinical data. Validation checks that the date is in the future and that the identifier exists in the records. If the date comes back in the wrong format, the system resends once with the validation error. If the identifier does not exist, there is no retry: the case goes to the front desk with the reason noted. The rescheduling is only written to the calendar after passing every check, and the write uses an idempotency key so a repeat does not duplicate the appointment. For the channel context, see automatic appointment scheduling on WhatsApp with AI.

Frequently asked questions

What is the difference between JSON mode and schema-constrained output? JSON mode generally aims to ensure the response is syntactically valid JSON, without guaranteeing which fields it contains. Schema-constrained output restricts generation to a structure you define, with fields and types. Check your provider's documentation for what each mode covers, and validate in your own code either way.

If the provider guarantees the schema, do I still need to validate? Yes. The schema handles shape, but it does not know whether the date makes sense, whether the customer exists in your records or whether the value was invented. In addition, truncated responses or refusals may not carry the complete object. Validation with business rules is your safety net.

How many retries should I allow? Few, with a fixed limit per workflow, and only for failures that could change on the second call, such as invalid format or a truncated response. After the limit, the case should go to a person or to a visible error state. Repeating endlessly adds cost and latency without improving the result.

How do I know structured output is good enough to expand its use? Agree on a success criterion beforehand and log failures by type during the pilot, using a test set that includes difficult inputs. If format failures are under control and rule and source failures fall to the review queue without causing harm, you have a basis to expand. If the logs show recurring schema or data problems, adjust before expanding.

Sources