PromptABCD
FeaturesLearnHow it worksUse casesFAQGuideBlogContext Blocks
Sign inGet started free
Sign inSign up
PromptABCD

A calm home for your best AI prompts. Save them once, find them in seconds, reuse them forever.

Product

  • Features
  • Chrome Extension
  • Free Courses
  • How it works
  • Use cases
  • Blog
  • Context Blocks
  • Export Anywhere
  • FAQ

Resources

  • User guide
  • Learn prompting
  • Sign in
  • Get started free

© 2026 PromptABCD. All rights reserved.

Privacy PolicyTerms and Conditions
Home/Blog/AI Harness/Validating Tool Arguments Before Execution
AI Harness

Validating Tool Arguments Before Execution

The model sent 2026-02-30 and the schema passed it. Agent tool argument validation is the layer that checks arguments against reality, not just against a shape.

September 8, 2026·9 min read
ShareShare
⚡Featured Prompt— copy and use right now
"delivery_date": {
    "type": "string",
    "pattern": "^\\d{4}-\\d{2}-\\d{2}$"   # still passes 2026-02-30
}

An agent at a logistics company called a tool named

schedule_delivery
with a date of
2026-02-30
. The tool's schema said the field was a string in date format, and
2026-02-30
is a perfectly well-formed date string. It just doesn't exist — February has no 30th. The schema passed it. The downstream system accepted it, silently rolled it to March 2nd, and a shipment went out two days late to a customer who'd paid for next-day. Nobody wrote a bug. Everybody trusted the schema. This is the gap that agent tool argument validation fills: the space between "well-formed" and "actually valid," which a schema alone can't see.

The lesson that team took away, and the one this case study is built around, is that a schema constrains what the model produces but guarantees nothing about what's correct. The model can hand you arguments that are schema-valid and semantically wrong all day long. The execution boundary is where you catch that — or don't.

The Problem This Team Faced

Their agent scheduled deliveries, updated inventory, and issued refunds. Each tool had a clean JSON schema, and they'd assumed — reasonably — that a schema-valid call was a safe call. The February 30th incident broke that assumption, and once they went looking, they found more like it.

A refund tool accepted any

order_id
string; the model occasionally passed an ID for an order that didn't exist, and the tool cheerfully tried to refund it. An inventory tool took a
quantity
integer with a
minimum
of 0; the model sometimes passed a quantity larger than what was in stock, and the tool let stock go negative. Every one of these calls was schema-valid. Every one was wrong.

⚠️ Common mistake: Treating schema validation as argument validation. The schema checks shape — types, formats, ranges the model sees. It cannot check meaning: whether a date exists, whether an ID refers to something real, whether a quantity is available. Confusing the two is how well-formed nonsense sails straight into your production systems.

The Wrong Approach

Their first fix was to tighten the schemas. More patterns, more constraints, more enums. It helped at the margins and missed the core problem, because the failures weren't shape failures.

hljs python
[object Object],: {
    ,[object Object],: ,[object Object],,
    ,[object Object],: ,[object Object],   ,[object Object],
}

What this does: Enforces a YYYY-MM-DD shape with a regex. It rejects

March 5th
and
2026/02/30
, which is genuinely useful — but
2026-02-30
matches the pattern perfectly and is still a date that doesn't exist. No regex can encode "February has 28 or 29 days depending on the year." Shape validation has a ceiling, and semantic correctness is above it.

The deeper issue was location. All their validation lived in the schema, which is enforced by nobody — the model reads it as a suggestion and mostly complies, but "mostly" is exactly the set of cases that hurt. They had no validation layer that ran, in their own code, before the action.

The Correct Approach

The fix was a dedicated validation step between the model's tool call and the tool's execution — a function that runs in the trusted harness, parses and canonicalizes the arguments, and checks them against reality, not just against a shape.

hljs python
[object Object], datetime ,[object Object], date

,[object Object], ,[object Object],(,[object Object],):
    ,[object Object],
    ,[object Object],:
        y, m, d = ,[object Object],(,[object Object],, args[,[object Object],].split(,[object Object],))
        parsed = date(y, m, d)          ,[object Object],
    ,[object Object], ValueError:
        ,[object Object], ArgError(,[object Object],)
    ,[object Object],
    ,[object Object], parsed <= date.today():
        ,[object Object], ArgError(,[object Object],)
    ,[object Object], parsed.weekday() == ,[object Object], ,[object Object], ,[object Object], ctx[,[object Object],]:
        ,[object Object], ArgError(,[object Object],)
    ,[object Object], {,[object Object],: parsed.isoformat()}    ,[object Object],

What this does: Parses the date into a real

date
object, which throws on February 30th because the type itself knows the calendar. Then it applies domain rules the schema can't express — no past dates, no Sunday delivery on routes that don't support it — and returns a canonicalized value. The impossible date is caught at step one; the business-invalid dates are caught right after. None of this fits in a schema, and all of it runs in code that actually executes.

The refund and inventory bugs got the same treatment: validation that checks referential integrity, not just format.

hljs python
[object Object], ,[object Object],(,[object Object],):
    order = ctx[,[object Object],].get_order(args[,[object Object],])
    ,[object Object], order ,[object Object], ,[object Object],:
        ,[object Object], ArgError(,[object Object],)
    ,[object Object], args[,[object Object],] > order.total:
        ,[object Object], ArgError(,[object Object],)
    ,[object Object], args

What this does: Confirms the order actually exists before refunding, and that the refund doesn't exceed what was paid. These are checks against state, not shape — the schema has no way to know whether an ID is real, because that answer lives in your database, not in the argument. Validation is where the argument meets reality.

⚡ Pro tip: Make validation return a canonicalized version of the arguments and have the tool use that, not the raw input. Parsing

2026-02-30
into a
date
and passing the ISO string downstream means every consumer gets the same clean, verified value — and the act of parsing is itself a validation. If it parsed, it's real; if it's real, everyone downstream can trust it.

Results and What Changed

The February 30th class of bug disappeared entirely, because the validation layer parses every date into a real

date
object and impossible dates can't survive that. More broadly, the team drew a hard line: the schema is what the model sees, and the validator is what actually runs, and the validator trusts nothing the schema "guaranteed."

Their error rate on tool calls dropped, but the more valuable change was where failures happened. Before, bad arguments failed deep in a downstream system, or worse, succeeded with wrong data. After, they failed loudly at the validation boundary with a clear message the model could read and correct — "order does not exist," "date is in the past" — turning silent data corruption into a recoverable retry.

⚡ Pro tip: Return validation errors to the model as tool results, not as exceptions that kill the run. "ArgError: order 4471 does not exist" is something the model can act on — it can look up the right order and try again. A validation layer that talks back to the model turns most bad arguments into a self-correcting loop instead of a dead run.

How to Apply This to Your Situation

For each tool, write down the difference between "shape-valid" and "actually valid." Shape-valid is what the schema enforces. Actually-valid includes: does this refer to something real, is this value available or in range against current state, does this combination of fields make sense together, and is this operation allowed right now. Everything on that second list is validation-layer work.

Then build one validation function per tool that runs before execution, parses into real types (which catches a surprising amount for free), checks against your domain and your data, canonicalizes the output, and returns errors as messages rather than crashes. Keep it in the trusted harness — never rely on the model or the schema to do this, because the model can be wrong and the schema doesn't run.

The pattern generalizes beyond these examples. A calendar agent validates that a room is free before booking it. A finance agent validates that an account has funds before transferring. A support agent validates that a user owns a subscription before canceling it. In every case, agent tool argument validation is the layer that checks the argument against the world, not just against itself, and in every case it's the difference between an error the model can recover from and a wrong action nobody notices until a customer complains.

Ordering Checks and Guarding Against Replays

Once you have a validation layer, the order of the checks inside it matters more than it looks. Run the cheap, self-contained checks first — parse the types, check ranges — before the expensive ones that hit your database or an external service. There's no reason to query whether an order exists if the amount field didn't even parse as a number. Fail fast on the cheap checks, and you spend database round-trips only on arguments that already cleared the basics.

Agent tool argument validation also has to think about repetition. Agents retry, and a retried tool call can arrive twice — the model didn't see the first result, so it asks again. For anything that changes state, that means "refund order 4471" can fire twice and refund twice. The validation layer is a natural place to enforce idempotency by checking whether this exact operation already happened.

hljs python
[object Object], ,[object Object],(,[object Object],):
    op_key = ,[object Object],
    ,[object Object], ctx[,[object Object],].seen(op_key):
        ,[object Object], ArgError(,[object Object],)
    order = ctx[,[object Object],].get_order(args[,[object Object],])
    ,[object Object], order ,[object Object], ,[object Object],:
        ,[object Object], ArgError(,[object Object],)
    ctx[,[object Object],].mark(op_key)
    ,[object Object], args

What this does: Builds a key from the operation and its arguments, and refuses if that exact operation was already recorded — so a duplicate refund request is caught at validation instead of double-charging. Putting the idempotency check in the validator means every state-changing tool inherits replay protection in the same place it inherits everything else, rather than each tool reinventing it.

⚡ Pro tip: Derive the idempotency key from the meaningful arguments, not from a request ID the model generates. The model may produce a new request ID on each retry even though it's semantically the same operation — so a model-supplied ID won't catch the duplicate. A key built from "refund + order + amount" catches it because the meaning is identical even when the wrapper isn't.

Next Steps

Argument validation pairs naturally with structured tool schemas — the schema shapes what the model attempts, the validator verifies what it produced. Together they're two layers of the same defense, and neither is sufficient alone.

Keep your per-tool validators versioned alongside the schemas and prompts they guard in a library like PromptABCD, so the domain rules the team learned through incidents — no past dates, no phantom orders, no overdrawn refunds — travel with the tools and don't have to be rediscovered, one late shipment at a time, in the next agent someone builds.

ai-harnessvalidationtool-argumentscanonicalizationreferential-integritysafety

Continue Reading

Managing Prompt Templates Across a Harness Codebase
AI Harness

Managing Prompt Templates Across a Harness Codebase

Four divergent copies of one prompt caused a two-day bug. Harness prompt templates management makes prompts versioned, tested, single-source artifacts instead of scattered strings.

September 10, 2026·8 min read
How to Open-Source Your Agent Harness
AI Harness

How to Open-Source Your Agent Harness

An agent harness isn't an ordinary library — it's security-sensitive infra tangled with your secrets. Release an open source agent harness without leaking a key or shipping unusable code.

September 10, 2026·8 min read
Error Taxonomy: Classifying Harness Failures
AI Harness

Error Taxonomy: Classifying Harness Failures

When every failure looks the same, you can't retry, route, or alert correctly. Agent harness error classification gives failures types that drive real behavior.

September 10, 2026·8 min read

Save the prompts from this post

PromptABCD is a free prompt manager. Paste, organize, and reuse your best AI prompts — no more hunting through chat history.

Start free →
← PreviousStructured Tool Schemas in the HarnessNext →How to Stream Harness Output to a UI
Share this post:
ShareShare