Solution Patterns & Playbooks

Output Validation and Self-Correction Loops

Model output that feeds another system must be checked by machine before anything downstream trusts it. The design questions are what to validate, when to let the model fix its own mistake, and when to stop trying.

Claude 3P 101 · Updated July 2026 · Unofficial guide

Every production pipeline that consumes Claude's output needs a validation gate: a deterministic check between the model and whatever acts on its answer. The modern starting point is to make most format failures impossible at the source, then design a small, bounded loop for the failures that remain — and a firm rule for handing the rest to a human.

Prevent first: structured outputs and strict tools

Structured outputs (output_config with a JSON schema, objects set additionalProperties: false) constrain generation so the response conforms to your schema; the companion feature, strict: true on a tool definition, guarantees schema-valid tool names and inputs. Together they eliminate the classic "invalid JSON, missing field" retry loop. Both are generally available on the Claude API, Claude Platform on AWS, Amazon Bedrock, and Vertex AI, and beta on Microsoft Foundry — so this pattern's foundation travels across all four 3P platforms. Two operational details: grammar compilation adds latency to the first request with a new schema and is then cached for 24 hours from last use, so don't churn schemas casually; and some JSON Schema features (numeric ranges, string length bounds, recursive schemas) are unsupported and return a 400 — those constraints move into your post-validation layer.

Validate what schemas can't say

A schema-valid response can still be wrong. Your validation gate should check, in code: business rules (totals reconcile, dates are ordered, IDs exist in your database), the constraints structured outputs could not express (value ranges, string lengths), and referential claims (a quoted source string actually appears in the input). The Python SDK's client.messages.parse() helper covers the first layer for free — it takes a Pydantic model, auto-transforms unsupported constraints, and validates the response client-side. Everything past that is ordinary code you write once and trust forever.

The self-correction loop

When a validation rule fails, the cheapest fix is often to show the model its own output and the specific error, and ask for a corrected version. Keep the loop tight and bounded:

for attempt in range(2):  # bounded: never loop unbounded
    resp = generate(messages)
    errors = validate(resp)      # deterministic checks
    if not errors:
        return resp
    messages.append({"role": "assistant", "content": resp.text})
    messages.append({"role": "user",
        "content": f"Validation failed:\n{errors}\nReturn a corrected version."})
escalate(resp, errors)           # human queue, with evidence

Feed back the specific failed rule, not "try again" — the correction rate difference is the whole pattern. Cap attempts at one or two; if the same rule fails twice, the problem is usually the input or the prompt, and more retries burn tokens re-proving it.

Know your stop reasons and error classes

Not every bad output is a validation problem, and reacting uniformly wastes money. Route by signal: stop_reason: "max_tokens" means truncation — retry with a higher max_tokens, not a correction prompt. stop_reason: "refusal" arrives as HTTP 200, you are billed, and output may not match the schema — do not self-correct; route to review, because the model is declining the task, not fumbling the format. Transport-level failures have their own rules: 429s, 5xx errors, and connection errors are retryable, and the official SDKs already retry them with exponential backoff (honoring the retry-after header), while 400-class errors are not retryable — fix the request. Your correction loop should trigger only on semantic validation failures; everything else has a cheaper, more specific remedy. Tool execution failures, similarly, go back as a tool_result with "is_error": true and an informative message, which is the tool-use ecosystem's native self-correction channel.

Escalate, don't loop, when: the same rule fails after one correction; the response is a refusal; the failure involves money, access, or anything irreversible; or correction cost is approaching the cost of a human doing the task. A bounded loop with a good escalation queue beats a clever loop every time.

Where to go next

See strict tool use for the tool-side guarantees, accuracy-first extraction for confidence scoring upstream of validation, and idempotency and retry for the transport layer. The feature matrix lists platform support.

Sources