Validation & Retry Loops

A validation-retry loop turns extraction failures into self-correcting workflows by feeding Claude the original document, its failed output, and the specific validation error.

Lesson 22 of 3073% of the guide
Prefer to learn by doing?

Even with tool use, production extraction produces semantic errors that schemas cannot catch. A validation-retry loop wraps each call: validate the output, and if it fails, send Claude a corrective retry. Done well, this converts one-shot failures into a resilient, self-correcting pipeline.

The retry-with-error-feedback pattern

An effective retry returns three things to the model: the original document (source to re-examine), the failed extraction (what it produced), and the specific validation error (exactly what was wrong). The specific error is the critical piece — without it, the model has no guidance and usually reproduces the same mistake.

The error is the guidance

A naive retry that just says 'try again' reproduces the mistake. Include the precise, per-field validation error so the model knows what to fix.

Blind retry vs error-feedback retry
Don't

Retrying with no diagnosis gives the model nothing new, so it usually reproduces the same wrong output.

messages.append({"role": "user",
  "content": "That was wrong. Try again."})
Do

Return the specific validation error (plus the document and failed output) so the model corrects exactly the offending fields.

messages.append({"role": "user", "content":
  f"Error: {err}\nRe-examine the document and "
  "fix only the fields in this error."})
from pydantic import BaseModel, model_validator

class Invoice(BaseModel):
    line_items_total: float
    stated_total: float

    @model_validator(mode="after")
    def totals_match(self):
        if round(self.line_items_total, 2) != round(self.stated_total, 2):
            raise ValueError(
                f"line_items_total {self.line_items_total} != "
                f"stated_total {self.stated_total}"
            )
        return self

def extract_with_retry(document, max_attempts=3):
    messages = [{"role": "user", "content": document}]
    for attempt in range(max_attempts):
        raw = call_claude_tool(messages)  # returns tool_use input dict
        try:
            return Invoice(**raw)
        except ValueError as err:
            messages.append({"role": "assistant", "content": str(raw)})
            messages.append({
                "role": "user",
                "content": (
                    "Your extraction failed validation.\n"
                    f"Error: {err}\n"
                    "Re-examine the original document and correct only the "
                    "fields involved in this error."
                ),
            })
    raise RuntimeError("Extraction failed after retries; route to human review")

When retries help and when they don't

The exam heavily tests distinguishing fixable from unfixable failures. Retries work when the information exists but was processed incorrectly; they cannot conjure information that is not there.

Failure typeRetry outcome
Format mismatch (date/currency)Fixable — retry with error
Value in wrong field / bad nestingFixable — retry with error
Missed line item skews the totalFixable — retry with error
Field genuinely absent from sourceUnfixable — flag for human review
Data only in an external documentUnfixable — flag for human review
Exam trap

Not every retry succeeds. If the source truly lacks the information, retrying just burns tokens — flag it for human review instead. Document A (line items sum to £450 but stated total is £500) is a retry; Document B (department field absent) is a human-review flag.

Build validation into the schema

  • Calculated vs. stated totals — extract both the model's computed sum and the document's stated total; any discrepancy auto-flags without extra logic.
  • Conflict-detection booleans — add a flag for contradictory source data (e.g., '30 days' in one section, 'net 60' in another) so the model surfaces the conflict instead of silently picking one.

Syntax, semantics, and Pydantic's dual role

Schema syntax errors (malformed JSON, wrong types) are eliminated by tool use. Semantic validation — cross-field arithmetic, date ordering, business rules — cannot be expressed by a JSON schema and needs external logic. This is where Pydantic earns its place: a Pydantic model enforces structure *and* semantics in one object, and its ValidationError produces machine-readable, per-field messages that drop straight into the retry prompt.

Tip

Pydantic is not redundant after tool use. Tool use covers structure; Pydantic expresses the cross-field rules schemas can't, and its errors are exactly the feedback your retry loop needs.

How the exam will try to trick you

The distractors below look right under time pressure — learn the tell.

  1. The trap

    Retry any validation failure — retries always eventually succeed.

    Correct answer

    Retry only when the information exists but was processed wrong; flag genuinely absent data for human review.

    Why: A retry cannot conjure information that is not in the source — Document A (totals mismatch) retries; Document B (field absent) is a human flag.

  2. The trap

    Retry with a generic message like that was wrong, try again.

    Correct answer

    Return the original document, the failed extraction, and the specific validation error.

    Why: Without the precise error the model has no guidance and reproduces the same mistake.

  3. The trap

    Treat tool_use JSON-schema validation as the complete solution.

    Correct answer

    Add semantic validators (e.g. Pydantic) for cross-field business rules on top of the schema.

    Why: Schemas catch syntax errors but cannot express rules like matching sums or ordered dates.

Key takeaways

  • A retry must include the original document, the failed output, and the specific validation error.
  • Without the specific error, retries usually reproduce the same mistake.
  • Retries fix format, structural, and arithmetic errors — not genuinely absent information.
  • Flag unfixable cases (missing source data) for human review instead of looping.
  • Extract calculated and stated totals, plus conflict-detection flags, to self-validate.
  • Pydantic enforces structure and semantics and emits per-field errors ideal for retry prompts.

Frequently asked questions

How do you make Claude self-correct extraction errors?+

Wrap the call in a validation-retry loop: validate the output, and on failure send Claude the original document, its failed extraction, and the specific validation error so it can correct exactly the fields that were wrong.

When should a retry loop give up and flag for human review?+

When the failure is unfixable — the required information is genuinely absent from the source or lives only in an external document the model never saw. Retrying those cases just reproduces the gap; route them to a human instead.

Practice makes pass

Ready to test what you just learned?

Reading gets you familiar — answering questions gets you certified. Jump into free practice or sit a full timed mock exam, scored 100–1000 just like the real thing.

No sign-up required · Explanation for every answer · Works offline