Error Contract Design for Agent-Callable Tools
Precise tool contracts prevent silent failures and make agent reasoning recoverable.

Most agent failures begin at the tool boundary, where unclear inputs and ambiguous results leave the model without a sound next move. Governed interfaces make those failures visible and recoverable. A purpose-built runtime such as UFO can enforce those contracts beneath the reasoning loop, so the model has safeguards it would otherwise have to make up for.
Why most agent failures are a tool design problem, not a model problem
When an agent chooses the wrong action, blaming the model is the familiar response. Yet the model plans from the tool name, description, parameter schema, and returned payload it receives. If those signals are vague, its reasoning starts with an interface that cannot reliably distinguish a valid action from a plausible mistake. Stronger models reduce some tool-call errors, but they cannot reliably repair that missing distinction.
This changes where engineering effort belongs. Elaborate reasoning layers above under-specified tools leave the source of the failure intact. The first task is to make the interface precise enough that the caller can tell what is allowed, what happened, and what can happen next.
What "silent failure" means in a running agent and why it compounds
A silent failure occurs when a tool returns a well-formed response that conceals a bad outcome. A cached error page, truncated list, or stale value can arrive in the expected format and become the basis for the next action. Unlike a clear timeout, this gives the reasoning loop no failure branch to take. The agent keeps moving, but its foundation has shifted.
The refund duplication incident shows how uncertainty about an outcome compounds. The tool retried after a transient timeout, and the agent’s retry loop retried too. There was no idempotency key, so every retry became a real refund. The timeout did not establish whether the write had happened, and neither retry layer had a guarantee against repeating it.
Unbounded loops accumulate cost without crashing, while stale OAuth tokens or expired API keys leave agents producing outputs built on failed calls. A running process is therefore not evidence of a successful workflow.
Design by Contract applied to agent-callable tools
Bertrand Meyer formalized preconditions, postconditions, and invariants for software interfaces in 1986. For agents, these rules give each tool a governed contract with a caller that is non-deterministic. Preconditions define what the caller must supply through a typed, constrained input schema. Postconditions define what the tool guarantees through an output schema that accounts for partial, empty, and cached results. Invariants define what must always hold, including resource caps, authorization scope, side-effect class, and idempotency guarantees.
The operational contract extends beyond data shape. It also declares timeouts, retry behavior, possible human approval, and what enters the run record. A description helps the model understand the tool, but enforcement belongs in code. Otherwise, a confusing parameter invites the agent to invent the context that a human integrator would ask someone to clarify.
Typed input schemas that make invalid states impossible
Consider an input named amount with a value of 500 supplied in place of one named amount_cents with a value of 50000. In the failure case, the JSON parses and the tool executes, but the customer receives the wrong refund. A loose schema leaves the unit implicit. A strict schema requires amount_cents, constrains its value, and rejects the unexpected amount field. Its model-facing description must also state that the amount is in cents, so the explanation and the enforcement agree.
Closed sets belong in enums, and scalar values need bounds, formats, and patterns where those apply. When additionalProperties is set to false, extra keys trigger validation failures. Required fields limit invented context, and explicit ISO 8601 formats avoid the ambiguity of “date string.” Record-count ceilings likewise belong in the schema.
Provider-side structured outputs help with formatting but do not replace server-side validation before execution. In a tool-connected environment such as UFO’s, a validator in the agent harness rejects invalid inputs at the boundary.
Tool naming and single-responsibility boundaries that prevent mis-selection
Schema validation only helps after the agent selects the right tool. Once it chooses, it fills in arguments for that tool, which may serve a different task from the one the user intended. Names must therefore make it clear how similar tools differ, without leaving that work to the description. Mixing naming patterns or offering both get_invoice and fetch_invoice creates needless ambiguity. By contrast, invoice_search and invoice_search_archived put the scope directly in the name.
Single-responsibility tools are easier to choose between and make it clearer how to handle errors. Descriptions should explain when to use a tool and when not to, such as reserving search_orders for a customer email and get_order for an exact order ID. Shell, filesystem, and browser tools may justify constrained multi-action interfaces because their underlying action spaces differ. As the catalogue grows, namespacing, phase-based exposure, and routing can narrow the visible choices.
Structured error payloads the agent's reasoning loop can act on
A bare 400 Bad Request says the call failed but gives the model no next step it can trust. It may make the same call again, make up another invalid call, or claim success it cannot see. The minimum useful payload has four fields, namely error_code, message, recoverable, and suggested_action. Together, they identify the failure, explain it for people, indicate whether recovery is possible, and specify what should happen next.
The distinction between explanation and direction matters. A message saying that input is invalid is not enough to repair it. A recoverable error must identify what was wrong, where it was wrong, and what a valid call looks like, through machine-readable fields and a plain sentence.
An operational payload also needs a distinct error class, a retryable flag, and a retry_after_ms hint. Rate Limiting (429), Authentication Errors (401, 403), Service Errors (500), and Resource Not Found (404) require different responses. Recovery may require changing the call or repairing authorization. Conversely, an HTTP 200 with an empty payload or buried warning still needs explicit treatment in the output contract.
Idempotency as a first-class tool contract, not a retry afterthought
When both the tool and the agent retry a write, each try can cause another side effect. The refund incident was therefore not a hallucination but a missing contract guarantee. Every side-effecting call should require an idempotency_key, and a call without one should be rejected before it runs. The server uses that key to detect duplicates, prevent repeated writes, and return the first successful result.
A named verification read gives the agent a way to confirm the write without repeating it. That read contract complements idempotency, but neither replaces controls on the original action. Read-only defaults, dry runs for irreversible writes, and explicit approval for actions that cannot be undone are enforceable permissions that limit how much harm a call can do.
How MCP's explicit handle model changes error contract design
MCP’s 2026-07-28 specification moves session state from invisible transport metadata into explicit handles. A server returns an identifier from one tool call, and the model passes it back as an ordinary argument in later calls. The model can then reason about that identifier and carry it between workflow steps. The error contract must therefore cover the handle lifecycle as well as whether the arguments are valid. Even when the argument shape is valid, the state it points to may not support the operation.
MCP also distinguishes protocol-level errors from tool-execution errors. A clean protocol response can contain a tool result with isError set to true, which still means the operation failed. The runtime must check both layers before it records success. MCP supplies discovery and invocation interoperability, while schema enforcement remains an added layer.
State coherence as a precondition for meaningful error recovery
Even a precise error cannot guide recovery if the agent no longer knows where it is in the task. Runtime state keeps track of the current node, earlier tool calls, and intermediate results. Knowledge memory holds what was learned across sessions, such as preferences and facts about the user. These serve different purposes, and remembering the user does not provide a checkpoint for a crashed workflow.
In practice, the memory layers include working memory, conversation summaries, task artifacts, and user or organization preferences kept over time. For error recovery, the immediate requirement is durable, inspectable task state with a known point from which execution can continue. Writing state after each step makes that recovery point available. Without an explicit state machine, recoverable set to true describes a possibility the agent has no route to take.
Retry logic, circuit breakers, and bounded retry policies at the tool runtime layer
Retry decisions belong in the tool runtime. Otherwise, the agent’s retry and the tool’s own retry can compound independently after the same timeout. The runtime can instead interpret the error class, enforce the retry bound, and apply exponential backoff. A retry_after_ms hint lets it delay a request rather than immediately returning the problem to the model.
During a hard external outage, a circuit breaker prevents repeated calls from becoming a retry storm. This protects both the external service and the internal token budget. Each tool contract should declare a timeout, maximum retries, and maximum cost per invocation. Branching, step limits, and cancellation at the orchestration layer keep the policy bounded even when the agent would otherwise continue trying.
Observability at the tool boundary: what to log and why the trajectory matters
Most silent failures are first caught by a human reading the output, after the cost has already been paid. Tool-boundary observability reduces that dependence by recording what happened at each step. The record needs the tool name, schema version, arguments, result, error class, identity, approval state, duration, cost, and cache status. Schema versions matter because a change in output shape can break the reasoning that follows while the process keeps running.
UFO, a workspace-based AI platform for teams, is built to show and log the structured signals agents get, helping teams catch silent failures and credential drift before they build on each other. The broader requirement is trajectory observability, which follows process health and the reasoning-to-action chain. Reviewing a sample of traces and watching for failures at each tool can reveal production drift even when evaluations pass. This audit trail is the operator’s half of the contract, because a tool’s promise to emit a signal matters only if someone examines it.
Running governed tool interfaces on a purpose-built agent runtime
A purpose-built runtime is the strongest fit when the need is to enforce the whole contract beneath the reasoning loop. UFO provides this infrastructure layer through input and output validation, bounded retries, backoff, circuit-breaker policies, persistent state, and step-level audit logging. When tools connect to UFO, schema checks happen before their inputs or results can become an unchecked basis for further reasoning. A recoverable failure has a checkpoint, while each call leaves the run record specified by the contract.
Other approaches address parts of the same problem. Graph-based durable execution persists state transitions and supports replay from a prior node after failure. A specialized runtime assurance layer focuses on execution reliability and can sit beside a runtime. General-purpose cloud infrastructure can also be adapted, but it does not enforce these agent-specific contracts by default. The useful comparison is therefore which guarantees an approach supplies and which the team must add.
A practical checklist for designing an error contract before a tool ships
Before an agent can use a tool, the tool needs contract terms that cover the ways it can fail. Wrong selection calls for a distinct name and explicit usage boundaries. Malformed arguments call for strict schemas, enums, bounds, and a deliberate required-field split. To catch silent bad results, output rules need to cover empty, partial, and cached responses. When a failure is unclear, structured error fields need to tell the agent how to recover.
Duplicate writes call for required idempotency keys, while excessive impact calls for declared side effects, authorization scope, resource caps, dry runs, and approval gates. Retry storms call for timeouts, retry ceilings, and cost budgets. To piece together an incident, teams need versioned records for each step and a review of the trajectory. Passing a demo does not establish any of these guarantees. The work is not convincing the model to be careful, but making the tool surface impossible to misuse silently.


