Security

When a Repair Changes the Meaning: Validating Corrected Arguments

Antonio Evans7 min read

A split scene: on the left, a request payload with one field highlighted and replaced; on the right, the resulting action shown as a receipt with a changed amount.
Key points

Key points

  1. 1An automatic repair can pass every type check and still change what the action does, so validation needs a schema layer and a semantic layer.
  2. 2Subtle drift shows up in currencies, date formats, units, enum aliases and recipient identity, and each one needs its own check.
  3. 3A repair pipeline should keep the original value, the repaired value and a derived canonical form, then compare meaning rather than text.
  4. 4Irreversible or money-moving actions should require confirmation when a repair changes an entity, a unit or an amount beyond a tolerance.

Why a valid repair can still be wrong

Automated argument repair sits between a model's intent and a real side effect. A retry loop notices that a tool call failed validation, rewrites the arguments, and sends them again. The second call is often cleaner, better typed and perfectly schema-valid, and it is still the wrong instruction.

This is a narrower problem than the wide field of semantic drift, which describes meaning diverging from intent gradually, across sessions or after model and prompt updates, without any error being raised (Dataiku). A repair is a single, deliberate edit at one moment. But it inherits the same property that makes drift hard to catch: the output looks correct. Nothing crashes. The tool returns a plausible result. The meaning moved.

The distinction between syntactic and semantic validation is the useful frame here. Syntactic validation checks that data adheres to a prescribed format, while semantic validation checks that the data is valid in the context of the system and its business rules, for example that a date is not in the future (SKAO developer documentation). A repair that turns next Tuesday into an ISO date has done syntactic work. Whether that date is the Tuesday the user meant is a semantic question.

One more reason to care: error handling is not an edge case in production agents. Practitioners documenting tool call failures separate input validation failures, where the payload is structurally wrong, from logic errors, where the tool ran but returned something unusable, and they route each to a different recovery layer because the model has to reason about the first and the orchestrator can silently retry the second (n8n blog). Repair is the moment where the first category is handled, and where intent is most at risk.

The schema layer: necessary, not sufficient

Structured outputs and JSON Schema enforcement solve the format problem. A schema can require that amount is a number, that currency matches a three-letter pattern, that due_date follows a date format, and that certain fields are present. Native structured output modes and libraries that wrap them exist precisely because free-form parsing is fragile and models change behavior between versions (Agenta).

Schema enforcement is also cheaper than it looks. A validator that sits between the model and the tool turns malformed arguments into a rejected call with a clear error, instead of a crash or a silent misuse downstream (Micheal Lanham on schema gating). If you do nothing else, do this.

But the same sources are explicit about the limit. Valid JSON is not a contract with meaning, and strict schema output narrows the interface only within its supported keywords; a schema-valid customer ID, amount, date or resource can still violate domain rules (TrueFoundry). Concretely:

  • {"currency": "USD"} passes a pattern check. It does not prove the user meant US dollars rather than Canadian.
  • {"due_date": "2026-03-04"} passes a date format. It does not prove the original 04/03 meant March 4 rather than April 3.
  • {"amount": 1500} passes a type check. It does not prove 1500 is dollars rather than cents.

Each of those is a repair that a schema accepts and a semantic check should reject.

Five ways a repair changes the meaning

These are the drift cases worth building tests around. They are chosen because each passes at least one common schema check.

CaseOriginal valueRepaired valueSchema verdictWhat actually changed
Currency swap$100100 CADValid, pattern matchesAmount in a different currency, roughly a different sum
Date reformat04/032026-03-04Valid ISO dateDay and month may be transposed
Unit change1.5 (hours)1.5 (days)Valid number24x the intended duration
Enum aliasurgenthighValid member of the enumPossibly a lower priority tier
Recipient resolutionj.smithJordan Smith (contractor)Valid entity referenceA different person resolved

Take the currency case with numbers. Suppose the caller meant 100 US dollars. A repair that appends CAD produces a valid request whose settlement value is about 70 to 75 US dollars at typical exchange rates, which you should read from your own treasury feed rather than from memory. The tool call succeeds. The invoice is short by roughly a quarter. No error is logged, because nothing went wrong at the schema level.

The unit case is worse in multiplier terms. One and a half hours becomes thirty six hours. Any downstream capacity check that compares hours to a threshold will approve or reject on a number a factor of twenty four away from the intent.

The recipient case is the one that most resembles identity mistakes in agent systems. Agent frameworks retry calls by regenerating them from model reasoning, and a regenerated request is rarely byte-identical to the original, so text comparison is insufficient for spotting a duplicate or a substitution; you need an explicit key or fingerprint (Trussed). The same holds for a repaired recipient: you need to compare resolved identifiers, not strings.

A validation pipeline that compares meaning

The pipeline below runs after repair and before execution. It is deliberately boring: five gates, each of which can reject or escalate.

  1. Preserve the original. Store the raw argument, the repaired argument and the error that triggered the repair. Without the raw value you cannot run any semantic comparison later, and cannot audit what happened.
  2. Schema gate. Types, formats, required fields, enum membership. Reject on failure and return a structured error to the model rather than a stack trace, so the next attempt has something to reason about (n8n blog).
  3. Canonicalization gate. Convert both the original and the repaired value into one canonical form: minor currency units with an explicit currency code, ISO 8601 dates with a time zone, base units for quantities, resolved entity IDs for people and accounts.
  4. Equivalence gate. Compare canonical forms, not strings, with tolerances per field type. Currency must match exactly after conversion at a pinned rate. Dates must match to the day unless a time was specified. Quantities must match exactly. Entity IDs must be identical.
  5. Consequence gate. Ask what the action does if it executes. If the action is read-only and the repair is equivalent, proceed. If it moves money, sends a notification, deletes or creates records, require confirmation on any mismatch.

A compact implementation of steps 3 and 4:

def canonical_repair(original, repaired):
    return {
        "amount_minor": to_minor_units(repaired["amount"]),
        "currency": repaired["currency"].upper(),
        "due_date": to_iso_date(repaired["due_date"]),
        "duration_hours": to_hours(repaired["duration"], repaired["unit"]),
        "recipient_id": resolve_entity(repaired["recipient"]),
    }

def equivalent(a, b, tolerances):
    for field, tol in tolerances.items():
        if field.endswith("_id"):
            if a[field] != b[field]:
                return False, field
        elif abs(a[field] - b[field]) > tol:
            return False, field
    return True, None

Run this against the original arguments canonicalized the same way. Any field failing equivalence is a candidate for escalation, and the failing field name should be logged, because the distribution of failures tells you which repairs to stop attempting.

The framing matters: a tool call with a typed name and argument object describes an intended action, and runtime authorization decides whether that action may happen (TrueFoundry). Repair validation is the layer between "the arguments parse" and "this action is authorized", and it is where the meaning gets checked.

If you are running this across several model providers, note that structured output support and schema keyword coverage differ by provider, so the same repair code can behave differently behind each one. An OpenAI-compatible gateway such as Quanta Inference lets you keep one validation pipeline while switching the model underneath.

Scoping repairs by blast radius

Not every repair deserves the same scrutiny. Sort actions by reversibility and by whether an external party sees the effect.

Action typeExamplesRepair policy
Read-only lookupSearch, fetch record, countAuto repair on schema only, log the diff
Internal write, reversibleDraft save, tag, noteAuto repair when canonical forms match, else confirm
Money movementPayment, refund, transferNever auto repair amount or currency, always confirm
Identity or accessGrant, invite, reassignNever auto repair recipient, always re-resolve and confirm
Irreversible externalSend email, publish, deleteConfirm on any non-empty diff

This mirrors the least-privilege principle for agents: give each agent the minimum set of tools and actions its task requires, and insert human checkpoints for higher-risk or irreversible actions rather than allowing unattended execution (Okta, Trussed on semantic drift detection). Repair policy is part of that scoping. A tool that can only read should be repaired freely; a tool that can pay should not be repaired silently at all.

There is a cost angle too. Retries replay conversation history rather than a single failed step, so a modest per-step failure rate compounds into roughly two times the token spend on longer agent runs, and the bill is invisible on latency and error dashboards (Tianpan). Repairs that thrash, failing semantic equivalence and regenerating repeatedly, are a retry pattern by another name. Cap them per session.

Testing for drift before it ships

Build a repair test set from your own logs. For each historical repair, record the original argument, the repaired argument, the expected canonical equivalence verdict, and the action class. Then assert on the verdict, not on the strings. Two additional practices pay for themselves:

  • Golden pairs per field type. At minimum one pair for currency, date, unit, enum alias and recipient. Keep them in the repository and run them whenever the repair prompt, model or schema changes.
  • Post-update revalidation. After any model, prompt or tool change, re-test agent behavior before it reaches production traffic, since decision boundaries shift in ways that are not visible in normal monitoring (Trussed).

Keep a term owner for each argument type, the way organizations assign ownership of metric definitions when the same word means different things across systems (AtScale). A repair that resolves active customer one way in one tool and another way in the next is the same failure at a different layer, and it is fixed the same way: one canonical definition, checked at execution time.

If the affected data sits behind a database, push enforcement down to the data layer where it can be governed deterministically, which is the core argument for row-level access control in front of LLM-driven queries (Tinybird, Snowflake). Restricting which rows query users can see at the engine level means a mis-repaired filter argument cannot exfiltrate more than the caller is already entitled to.

FAQ

FAQ

Does schema validation alone make repairs safe?

No. A schema proves format, not intent. A repaired value can pass every type, pattern and enum check while changing the currency, the date, the unit or the recipient. Treat schema validation as the first gate and add a canonical comparison against the original argument before execution.

How do I detect that a repair changed a currency?

Canonicalize both the original and the repaired amount into minor units with an explicit currency code, then convert at a pinned rate if the currencies differ. If the converted values differ beyond a tolerance, reject the repair. Currency should never be inferred from a symbol alone, because the symbol is ambiguous across regions.

What should happen when a repaired recipient resolves to a different entity?

Escalate. Compare resolved entity identifiers rather than names or usernames, because a regenerated request is rarely textually identical to the original. For identity, access or money-moving actions, re-resolve the recipient and require explicit confirmation rather than accepting a repaired match.

Are repaired arguments a form of semantic drift?

They are a related, single-event version of it. Semantic drift describes meaning diverging gradually across sessions or after updates, without errors (Dataiku). A repair changes meaning in one step, which makes it easier to catch: you have the before and after values, so you can compare them directly.

Where does this pipeline fit if we run several models?

Put the validation in your own orchestration layer, not inside a provider, so the rules stay constant when the model changes. Then reuse the same test pairs across providers and re-run them after every model swap. For teams mapping out where repairs, agent permissions and review checkpoints belong, a free discovery call is a reasonable next step.

Sources

Sources

  1. Semantic drift in AI: how to detect, measure, and prevent it
  2. Semantic Validation, developer.skatelescope.org 1.13.0 ...
  3. LLM Tool Calling Error Handling: Retries and Fallbacks
  4. The guide to structured outputs and function calling with ...
  5. Stop Blaming the LLM: JSON Schema Is the Cheapest Fix ...
  6. LLM Structured Outputs with JSON Schema
  7. Tool Call Idempotency for AI Agents: A Technical Guide
  8. How to implement least privilege for AI agents
  9. Semantic Drift Detection in AI Agents: A Technical Guide
  10. Retry Budgets for LLM Agents: Why 20% Per-Step Failure ...
  11. What Is Semantic Drift in AI?
  12. Use RLAC to Secure LLM Database Access
  13. What Is Row-Level Security (RLS)? Benefits and Use Cases
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