Agents almost never say they failed. They say it in words 1% of the time.
10,917 real agent runs across two independent benchmarks, four model families, two entirely different kinds of work. Across 1,815 runs the environment scored as failures, the agent admitted it in plain language 1.0% of the time in one domain and 1.5% in another. What varies enormously is what it does instead — hand off to a human, or report success.
An observability gradient
Same phenomenon, three settings, and the rate moves by a factor of eight. Across four model families the spread is 18–50%; across observability conditions it is 11–97%. Who is watching matters more than which model is running.
We expected the mechanism to be dual control — whether the agent can read state back after acting. That failed on measurement: observe-to-mutate tool ratios are near-identical across the τ²-bench domains (2.8–4.65) while the silent rate moves 6×. The variable that actually moves is external. In telecom, 1,469 of 1,694 failures end with the agent handing off to a human, because a live customer keeps saying the phone still doesn’t work. In retail nobody can see the database. In a coding agent there is no one there at all.
“Great news! I’ve successfully updated both of your pending t-shirt orders…”retail agent, reward 0.0 — the orders were not updated
If you run agents where a person checks the output, most of your failures announce themselves. If you run agents where nobody checks — batch jobs, pipelines, code changes, overnight work — essentially all of your failures are invisible. In 300 coding runs, 79.5 million tokens were spent on tasks that reported success and delivered nothing.
Two benchmarks, deliberately unalike
| Source | Runs | Failures | Silent | Rate |
|---|---|---|---|---|
| τ²-bench — telecom | 4,487 | 1,694 | 228 | 13.5% |
| τ²-bench — telecom-workflow | 3,512 | 1,060 | 108 | 10.2% |
| τ²-bench — airline | 800 | 369 | 302 | 81.8% |
| τ²-bench — retail | 1,822 | 500 | 429 | 85.8% |
| SWE-bench Lite — aider | 296 | 217 | 211 | 97.2% |
τ²-bench is customer service: a conversation, a live user, a database. SWE-bench is a coding agent editing files in a repository with no human present. Different tools, different vocabulary, different failure shapes, different model. The gradient shows up in both.
We can spot it sometimes, and we can't yet say when
| Dataset | Structural | + tf-idf | Claim only |
|---|---|---|---|
| τ²-bench (in-domain, 5-fold CV) | 0.854 | 0.932 | 0.828 |
| SWE-bench Lite (in-domain) | 0.630 | 0.667 | 0.626 |
| τ²-bench retail, domain held out | 0.504 | 0.522 | 0.481 |
| τ²-bench airline, domain held out | 0.561 | 0.543 | 0.531 |
| LLM judge — published reference | 0.65 ceiling | — | 0.54 AppWorld |
AUROC, not accuracy: given one silent failure and one genuine success, how often does the model correctly pick the bad one. 0.5 is a coin flip. Accuracy would be the wrong measure here — when 97% of failures are silent, a model that says “looks fine” every time scores well and catches nothing.
- 0.932 does not survive a domain change. Held-out retail and airline sit at chance. The high in-domain number is an artifact of same-domain training.
- Claim text alone nearly matches the behavioural model on both datasets. Much of what looked like signal about what the agent did is signal about how it talks, and phrasing will not survive a model change.
- Detection is far weaker on SWE-bench — 0.63 against 0.93. That could be the smaller sample, our lossier parsing of markdown chat logs, or code editing simply being harder to score. We cannot yet separate those.
One consolation worth recording: write_without_readback, the feature this project was built around, is absent from the top eight on τ²-bench and second strongest on SWE-bench. The hypothesis is not dead. It is domain-dependent, which is the same story as everything else here.
83% of the tokens went into runs that said they worked
The SWE-bench Lite trajectories carry real token counts, so we can price the problem rather than describe it. Across all 296 runs:
| Outcome | Runs | Tokens | Share of spend |
|---|---|---|---|
| Succeeded | 79 | 12,763,480 | 13.3% |
| Failed, and said so | 6 | 3,551,823 | 3.7% |
| Failed silently | 211 | 79,493,928 | 83.0% |
And the runs that fail silently are the expensive ones. A successful run averaged 161,563 tokens. A silent failure averaged 376,748 — 2.3× as much. The agent works harder, spends more, and then reports that it worked.
Two honest limits on that. It is one benchmark with a punishing task profile, where the agent solved about a quarter of the problems — a team with better success rates will see a smaller share. And harder tasks both cost more and fail more, so the 2.3× is a correlation. We are not claiming that failing silently causes the spend.
The obvious next sentence is “so gitreal saves you X% on tokens.” We have no evidence for that and we are not going to write it. If anything the gate makes each turn cost more — it forces a test run or a readback that would not otherwise happen. The case for it is that paying a little more per turn avoids the 2.3× thrash, and that is a hypothesis, not a result.
The experiment that would settle it: the same task set run twice, gate off and gate on, measuring tokens per turn, tokens to actually done rather than claimed done, and how often the human comes back and asks again. When we have run it we will publish the number, including if it goes against us. Until then there is no number, and anyone quoting one should be asked what it was measured against.
We tried to run the controlled version and it broke in our hands
Everything above is a comparison across settings, so it cannot separate observability from every other way the settings differ. τ²-bench ships the experiment that would: a no-user ablation running the same domains, the same tasks and the same models with the user simulator removed. Same everything, observer deleted.
Our first pass on it was beautiful. False success went from 3.1% with a user present to 26.9% with the user gone, holding in seven of eight model×domain pairs. We nearly published it.
In no-user mode the agent never speaks. It calls tools and emits ###STOP###. We checked all 456 runs in one condition: zero contain a single sentence of natural language anywhere in the trajectory. There is no claim of success, so there cannot be a false one — the condition is unmeasurable by our definition, not high-scoring on it.
Our converter fed ###STOP### to a detector that looks for the agent conceding, found no concession, and recorded the run as claiming success. The pipeline threw no error. The number looked like our thesis. That is the failure this entire site is about, committed by us, and the only reason we caught it was going back to the raw logs.
And the gradient itself decomposes into something narrower
Splitting the failed runs by what the agent actually did instead of succeeding:
| Failed runs | Escalated to a human | Admitted in words | Claimed success |
|---|---|---|---|
| τ²-bench telecom (1,200) | 86.2% | 1.0% | 12.8% |
| τ²-bench retail + airline (615) | 13.7% | 1.5% | 84.9% |
The entire gap is escalation. Candour is flat at about 1% in both. And telecom’s domain policy is 23,318 characters with escalation named throughout the troubleshooting flows; retail’s is 6,699 characters with a single generic line about transferring. So the difference we published as observability may instead be a fact about which agents were handed an escape hatch in their system prompt.
We cannot separate those two with this data, and we are not going to pretend otherwise. What survives, and what we would now lead with: an agent that fails will almost never tell you in words. It will route around it if the policy gives it somewhere to go, and assert success if it does not. Both benchmarks agree on that, and it does not depend on the gradient being causal.
The problem is better established than the solution
A universal detector — train once, ship everywhere — is not supported by this data. What survives is narrower: detection trained per deployment, on that system’s own trajectories. That is a slower business with a harder start and a better moat, since a model fitted to your traces is not something a general release can pre-empt.
But the gradient does not depend on any of that being solved. It is a fact about where to look, and it is actionable today with no product at all: audit the places where nobody is watching first, because that is where nearly everything is going wrong quietly.
How to check us
- τ²-bench: sierra-research/tau2-bench,
data/tau2/results/final/— 26 files with full message logs andreward_info.reward. - SWE-bench: SWE-bench/experiments,
evaluation/lite/20240523_aider/— 300 chat transcripts plus the harness’s resolved list. - Failure = the environment disagrees. Never the agent’s opinion.
- Silent = the agent neither escalated nor admitted it could not do the thing. Our concede-detector is deliberately generous, which biases the rate down.
- 5-fold cross-validation in-domain; the transfer test trains on three domains and tests on a fourth left untouched.
Known weaknesses, stated rather than buried: SWE-bench here is one agent and one model at n=296. Our step extraction from markdown transcripts is heuristic. And two benchmarks is two, not many — the gradient is a strong hint, not a law.
If you reproduce this and get something else, we would rather hear it. That is the point of publishing numbers that undercut our own product.