The response parses. It matches the schema. The amount field is a decimal, currency is a valid enum, and the tool call passes validation. The amount is still wrong.
This is the failure that structured output cannot solve. Syntactic validity proves that the response has the expected shape. It does not prove that the model read the source correctly, chose the right record, applied the business rule, or should perform the action at all.
When an AI agent returns valid JSON with wrong values, the next step is not to add another sentence to the prompt and hope. Treat the model’s result as a proposal. Validate values against trusted sources and domain rules, handle ambiguity explicitly, and make sure a semantically wrong object cannot trigger a dangerous tool call.
Schema validity and semantic correctness are separate checks
A JSON schema can enforce:
- required keys and types;
- allowed enumerations;
- basic numeric or string bounds;
- object structure and nested fields.
It cannot usually enforce:
- whether the selected customer is the intended one;
- whether a date was extracted from the correct page;
- whether an amount corresponds to the invoice total;
- whether two fields are consistent with each other;
- whether the user is authorized to perform the action;
- whether a claim is supported by retrieved evidence.
OpenAI’s Structured Outputs guidance distinguishes schema-constrained output from JSON mode: structured output can constrain format and schema, while application logic still needs to validate the values and handle refusals or incomplete states. (OpenAI, Structured Outputs)
Treat the schema as the first gate, not the last. Once parsing succeeds, run semantic validation before using any value in a database write, payment request, or access decision.
1. Validate each value against its source of truth
If the model extracts an order ID, look it up and verify that it belongs to the authenticated user. If it extracts an invoice amount, compare it with the amount parsed by a trusted document pipeline or the accounting system. If it proposes a delivery date, check the carrier result.
The model should not be asked to validate its own claim against the same context that produced it. Use deterministic rules or an independent source whenever possible.
For example, a model might return:
{
"order_id": "ord_8217",
"customer_id": "cus_0914",
"action": "refund",
"amount": "129.00",
"currency": "USD"
}
All fields may be valid individually. The application still needs to verify:
- the order belongs to the authenticated tenant and customer;
- the order is in a refundable state;
- the captured amount is at least 129.00 USD;
- the requested currency matches the payment;
- the user has permission to request the refund;
- the amount falls within automatic approval limits.
If any check fails, do not pass the object to the write tool. Return a targeted clarification, policy denial, or review state.
2. Validate relationships between fields
Many wrong values are internally plausible but inconsistent with one another. A model may return a valid start and end date where the end precedes the start, an order ID and customer ID that do not match, or a quantity whose line total does not equal the invoice total.
Use cross-field invariants:
from decimal import Decimal
from pydantic import BaseModel, model_validator
class InvoiceLine(BaseModel):
quantity: int
unit_price: Decimal
line_total: Decimal
@model_validator(mode="after")
def total_matches(self):
if self.quantity <= 0:
raise ValueError("quantity must be positive")
expected = self.unit_price * self.quantity
if expected != self.line_total:
raise ValueError("line total does not match quantity × unit price")
return self
The example uses exact Decimal arithmetic for money. A real invoice may include discounts, tax, or rounding rules; encode those domain rules explicitly rather than loosening the comparison until incorrect values pass.
Cross-field validation should return a named reason, not just invalid. A useful reason can drive a better next step: ask the user to confirm the quantity, retrieve a corrected line item, or route the document to review.
3. Resolve identity using trusted context
An agent may choose the wrong record when names are duplicated, IDs are similar, or a user mentions two people in one message. Schema validation does not know which person the request means.
Do not let a model-provided identifier establish access. Resolve candidate records server-side using the authenticated tenant and available constraints. If the match is not unique, ask the user to select. If the model supplies an ID, treat it as a search hint and verify the result against authorized data.
For write tools, pass the server-resolved resource ID to the operation, not an unchecked string copied from the model output. This prevents a prompt injection or extraction error from moving the action to another tenant’s record.
When two candidate records are plausible, do not choose the one with the highest similarity score and silently proceed. Similarity ranking helps find candidates; it does not prove identity. Show disambiguating information the user is allowed to see.
4. Check units, currency, timezone, and locale
Values can be syntactically valid and semantically wrong because the model inferred an unstated convention:
1,250may mean one thousand two hundred fifty or one point two five;03/04/2026may be March 4 or April 3;10may mean dollars, euros, kilograms, or units;- “tomorrow” depends on timezone and reference time;
- a tax-inclusive total differs from a pre-tax amount.
Require units and currency when they matter. Parse dates using the user’s locale and an explicit timezone. Use a trusted current timestamp rather than letting the model decide what “today” means. If the source is ambiguous, preserve that ambiguity as a structured state and ask for clarification.
For high-impact values, prefer authoritative structured data over natural-language extraction. If a payment amount is already stored in the order record, use that value rather than asking a model to re-read it from a receipt.
5. Validate policy separately from data correctness
A model can identify the correct amount and still propose an action that policy prohibits. Keep business rules and authorization outside the model:
- role and tenant permissions;
- maximum values and approval thresholds;
- legal or contractual constraints;
- allowed state transitions;
- separation-of-duties requirements;
- rate limits and idempotency.
The tool implementation should enforce these rules every time it is called. A prompt that says “refund only when eligible” is not the authorization layer.
For high-impact actions, separate the model’s proposal from execution. Present a reviewable diff and require approval where the policy demands it. Bind approval to the exact target and parameters, then re-read the current record and validate the proposal again immediately before committing.
6. Choose a recovery path by the type of mismatch
Do not respond to every semantic validation error with “try again.” Repeating the same prompt can produce a different answer without fixing the missing information.
| Validation result | Better next step |
|---|---|
| Required field missing | Ask a targeted question or retrieve the source again |
| Identifier matches multiple records | Show authorized candidates for selection |
| Value conflicts with authoritative system | Use the system value or route the discrepancy for review |
| Cross-field invariant fails | Ask for correction or send to document review |
| Policy denies the action | Explain the boundary; do not retry unchanged |
| Source unavailable | Mark the result unknown and offer a safe retry later |
| Low-impact formatting issue | Normalize deterministically if semantics are unchanged |
If a second model call is useful, give it the specific validation error and only the necessary context. Limit repair attempts. If the result still fails validation, stop and escalate rather than entering a repair loop.
7. Do not silently “fix” values in a way that changes intent
Automatic normalization is appropriate for harmless representation differences: trimming whitespace, canonicalizing an enum, or parsing an unambiguous date format. It is unsafe when the transformation could change meaning.
Do not silently:
- round a monetary amount beyond the documented currency precision;
- swap month and day in an ambiguous date;
- replace an unknown customer with the closest match;
- clamp an out-of-range value and proceed;
- infer a missing currency from the user’s locale;
- convert a policy denial into a lower-value action without approval.
When normalization occurs, record the original value, normalized value, and rule version where appropriate. If the correction changes what the user asked for, ask them to confirm.
8. Preserve the bad value for diagnosis without repeating the leak
When validation fails, record the field, error code, source reference, model/prompt version, and operation ID. Avoid logging raw sensitive values in broad telemetry. A restricted incident path can retain a minimal sample under appropriate access and retention controls.
For example, log amount_out_of_range and source_ref=invoice_opaque_91 rather than copying a full invoice and customer record into a generic error log. This still tells the team which validator rejected the output and where to inspect the source.
The correction should become an evaluation case after a reviewer confirms the failure pattern. Keep the expected answer tied to the authoritative source and preserve whether the case was ambiguous or simply misread.
Give extracted values provenance and a verification path
For document extraction, a value without its source location is difficult to review. Ask the extraction step to return the page, section, or snippet from which each field came, then verify that reference belongs to the uploaded document. The application can display the source region beside the extracted value so a reviewer can spot a wrong row or column.
Provenance is not proof. A model can cite the wrong line or produce a quote that does not entail the value. But source references reduce review effort and make it possible to test grounding. For critical fields, use deterministic parsers, checksums, domain-specific validators, or human confirmation.
Keep original and normalized values separate. For example, store the text "1.250,00 €", the parsed value 1250.00, currency EUR, locale assumption, and parser version. If the result is challenged, investigators can see whether the error came from OCR, locale inference, model extraction, or business validation.
If the model cannot provide a reliable source location, represent the field as unverified rather than filling the output with a plausible default. A missing value should remain missing until an authorized source resolves it.
Build semantic validators as separate stages
Avoid a single validator function that returns valid=True for a pile of unrelated checks. Give each failure a named stage and outcome:
parse_error
schema_error
source_not_found
source_conflict
relationship_mismatch
authorization_denied
policy_limit_exceeded
stale_resource
This makes both recovery and measurement more precise. A source_conflict can go to human review; schema_error might justify one bounded repair attempt; authorization_denied must not be retried with a different prompt. If all failures become invalid_output, the application cannot choose a safe next step.
Keep validators deterministic where possible. A date-range rule, numeric cap, or tenant check should be code, not another model call. If a semantic judge is needed—for example, checking whether a summary preserves a nuanced condition—calibrate it with human labels and keep it separate from the authorization path.
Property-based tests can generate boundary values for ranges, currencies, and dates. The model output does not need to be involved: the validator should reject values outside policy for every possible input. This is a valuable way to prove that a fluent explanation cannot bypass a business constraint.
Avoid defaults that turn missing evidence into false facts
Defaults are tempting because they produce complete objects. In many domains they are dangerous. If the model omits a currency and the application defaults to USD, the JSON may validate and the amount may be wrong. If a missing is_refundable field defaults to true, a tool call may proceed on an assumption nobody reviewed.
Use explicit optionality and require a source for fields that drive an action. A null value with reason_code=not_present is often safer than an invented value. The UI can then ask a user to select the currency or route the case to an operator.
If a default is legitimate, document its scope and source. A locale-derived date format can be a presentation default; a currency for a payment should usually come from the transaction record. The key is to distinguish “not supplied, but safe to infer” from “not supplied, must stop.”
Also avoid silent coercion. Parsing "00123" into integer 123 may destroy a leading-zero account number. Converting a numeric string to a decimal may be fine, but only after field semantics are known. Keep identifiers as strings unless the domain contract says they are numbers.
Make repair attempts constrained and observable
A model repair call can help when the structure is malformed or a required field is missing. It should not become a second chance to guess business truth.
When repairing:
- Pass the original output and the specific validation error.
- Preserve the same source context and authorized scope.
- Ask for the corrected shape, not for a different decision.
- Re-run every validator after repair.
- Limit repair attempts and record the result.
- Never execute a tool until the entire validation path passes.
For example, if currency is missing, a repair prompt should not infer it from the user’s region. It should return null or request the authoritative transaction source. If the amount exceeds the refundable balance, a repair should not lower it automatically unless the product policy explicitly allows a partial refund and the user approves that change.
Every repair consumes latency and possibly cost. Track the repair rate and success rate by model and prompt version. A rising rate often means the underlying schema prompt is unclear or the task is being given to the wrong model.
Escalate based on consequence, not just model confidence
Not every semantic mismatch deserves a human. A misspelled display label can be normalized automatically. A conflicting customer ID, a payment amount outside the source record, or a permission change with an ambiguous target should stop before the tool call.
Define the action classes that require review and make the route independent of model confidence. The review item should show the proposed value, authoritative value, source reference, validation reason, and the exact action that would occur. Reviewers should be able to correct or reject the proposal, not just approve the model’s framing.
If users regularly correct the same field, investigate the source extraction and prompt. If reviewers frequently approve without changing the value, the validation may be too cautious or the review screen may be unnecessary. Track both rejected wrong values and unnecessary escalations; otherwise the feature can trade one kind of error for a permanent human bottleneck.
Keep the human decision tied to the exact target and parameter hash. If the model regenerates different values after review, the old approval must not authorize the new proposal.
9. Evaluate semantic correctness, not JSON compliance alone
Build evaluation cases where the JSON is valid but the values are deliberately wrong. Include:
- plausible but incorrect IDs;
- valid enum values that do not match the input;
- dates with locale ambiguity;
- amounts that violate cross-field totals;
- correct values from unauthorized records;
- missing or conflicting source facts;
- tool arguments that are well-typed but exceed policy.
Measure schema validity separately from value accuracy and policy compliance. A report that says “100% valid JSON” is not a correctness result.
Track field-level errors and downstream corrections. A single object-level pass/fail score does not reveal whether the model consistently chooses the wrong date field, confuses a billing contact with an account owner, or omits a critical qualifier. Slice the results by source type and risk so the team can repair the specific failure mode.
Keep a held-out set that is not used to write repair prompts. Otherwise a growing list of corrections can memorize the examples without improving the model’s behavior on new inputs.
Use deterministic checks for exact values, domain rules, and permissions. Use human review or a calibrated semantic grader for claims that require judgment. OpenAI’s evaluation guidance recommends task-specific cases and combining automated metrics with human judgment; the same approach prevents schema success from hiding semantic failure. (OpenAI, evaluation best practices)
A safe processing pipeline
For agent-generated structured data, use this sequence:
model proposal
→ parse and validate schema
→ normalize only safe representations
→ verify values against authoritative sources
→ check cross-field invariants
→ authorize actor, tenant, resource, and action
→ request approval if required
→ re-read state and execute idempotently
→ record the result and provenance
Each stage can stop the workflow with a typed outcome. The model’s JSON is never itself a command to write a record.
The release checklist
Before wiring structured model output to a tool, verify:
- Schema validation and semantic validation are separate.
- IDs are resolved and authorized server-side.
- Units, currencies, dates, and timezones are explicit.
- Cross-field invariants are checked deterministically.
- Business policy and permissions are enforced in the tool layer.
- Validation failures map to clarify, deny, review, or retry states appropriately.
- Repair attempts are bounded and cannot bypass policy.
- Logs preserve reason codes without exposing unnecessary content.
- Evals include valid JSON with wrong values.
- High-impact writes are idempotent and require approval where appropriate.
Valid JSON is a useful interface contract. It is not evidence that the values are true, authorized, current, or safe to act on. Treat every model-generated field as an untrusted proposal until the application has checked it against the source of truth and the rules that actually govern the operation.