DEV Community

Cover image for Most agent failures are not hallucinations
Shaan
Shaan

Posted on Fully Autonomous

Most agent failures are not hallucinations

When an agent does something dumb, the usual reaction is "the model hallucinated" and a new line in the system prompt. A paper from this month argues that is usually the wrong diagnosis.

Tommy Tran's "Failure Is a Feature" in ACM Queue (October 15, 2026) starts from a plain observation. An agent is a loop. The model proposes an action, a harness runs it against some tool, the result goes back to the model. That is a coordinator making remote calls to services it does not control, over networks that drop things. It is a distributed system, with a nondeterministic part in the middle.

Distributed systems already have 40 years of answers for this. We just keep reaching for prompts instead.

Where agents actually break

The paper sorts failures by where they enter the loop:

  • Tool boundary: timeouts, 429s, dropped connections.
  • Payload: the call "works" but returns truncated JSON, an HTML error page, or a schema that drifted.
  • Model: wrong tool, invented arguments, declaring success without checking.
  • Side effects: the write went through, the acknowledgment got lost, the step was retried.
  • The loop itself: two tools ping-ponging, retrying a dead API all night, running out of context.

Only one of those is a model problem. The rest have boring, known fixes. A truncated response that says "balance": 1 instead of 1000 will happily get reasoned about by a model, because models parse everything. Nothing in the pipeline said no.

The experiment

The paper ships a small open harness so you can check the claims. Under a fixed fault profile, here is what happened:

Setup Tasks completed Side effects
Bare agent 33.3% not stated
Retries only 83.3% duplicate records created
All seven patterns 89.6% no duplicates, no orphaned writes

Retries alone look great until you notice the duplicates. A retry on a write that already succeeded gives you two refunds. The extra 6 points from the other patterns matter less than the cleanup they prevent.

Where faults enter the agent loop and which pattern covers each one

Three things worth stealing

1. The harness owns the idempotency key, not the model.
If you let the model fill in an idempotency_key argument, a retried step may come back with a different key and the dedup quietly stops working. Derive the key from something the harness controls:

def run_side_effect(task_id, step_id, tool, args):
    key = f"{task_id}:{step_id}:{tool}"  # same on every retry of this step
    return call_with_retries(tool, {**args, "idempotency_key": key})
Enter fullscreen mode Exit fullscreen mode

If the model replans and proposes the action again, that gets a new step id, which is correct. That is a new decision, not a resend.

2. Treat the token budget like a deadline.
Give every task a time limit, a token limit and a step limit, and check them before each new step. The paper calls the step cap the cheapest insurance in the article. A few lines turn "the agent looped all night against a dead API" into "the task failed at step 20 with a clear reason".

3. Keep the model out of the retry loop.
Let the harness retry transient errors with backoff and jitter, and show the model only the final result or a typed failure. Otherwise you pay tokens for every attempt and fill the context window with failure narration. For dead dependencies, a circuit breaker should return something the model can plan around, like {"error": "circuit_open", "retry_after_s": 30}.

The honest caveats

The paper is upfront that this is a toy agent with injected faults. Circuit breakers actually lowered task success when faults were random, and only pay off when a real dependency goes down. So this is not a recipe to paste in. It is a way to stop guessing.

The bigger point matches what the paper says about benchmarks like tau-bench. Single-run success rates flatter agents. An agent that works most of the time per attempt fails a lot more often when you need it eight times in a row. Reliability, more than raw capability, is what stands between a demo and something you can leave running.

If you are building agents, my suggestion is to read the paper and run the harness before you write your next prompt fix. The link is below.

Source: Tommy Tran, "Failure Is a Feature: Engineering Fault-Tolerant AI Agents", ACM Queue, October 15, 2026. https://queue.acm.org/doi/10.1145/3841558

Written with AI assistance.

Top comments (2)

Collapse
 
anciwasim profile image
Wasim Sheikh •

Keeping the key in the harness makes sense, since a model-supplied one can drift between retries. The part I'd poke at is the scope: with task:step:tool, a replan that re-proposes the same write gets a new step id, so a first attempt that actually landed wouldn't be caught by the key alone. Does the downstream system still dedupe on its own side, or do you check for the earlier write before the replanned call goes out?

Collapse
 
reidmarlow profile image
Reid Marlow •

The payload truncation point hits hardest in production. Models will happily hallucinate plausible fields over half-severed JSON or a 502 HTML body without throwing an error, because tokenizers treat raw HTML text as valid context. Putting a strict schema parser or validator in the harness that fails the tool call before the model ever sees the raw payload saves you dozens of wasted replanning turns.