A field guide to the ways my agents fail — collected from my own production stack, not from a blog post.
Most public "AI failure" writing is a listicle of things that could go wrong in theory. This is the opposite: a catalogue of things that did go wrong in a system I actually run — a fleet of more than two dozen agents, a nightly cron layer, two native apps, a public site, and a vault of standing rules that govern all of it.
The lineage I'm borrowing from is the naturalist compendium — 李时珍's 本草纲目 (Compendium of Materia Medica), where each specimen is named, drawn, placed in a family, and described by how you recognize it in the wild and what it does to you. A failure taxonomy is the most expensive document a practitioner owns, because every entry cost real operational hours to collect. You cannot write this page from imagination. That is the entire point.
Each entry below is a species of failure, grouped into a genus. Every one has at least one specimen — a dated, documented instance in /incidents. Where a species is a known risk I have not yet collected a specimen for, I say so plainly rather than invent one. A field guide that lists animals it never saw is not a field guide.
How to read an entry
Field marks — how you recognize it while it's happening
Habitat — where in a stack it lives
What it costs — the operational bill, from my own logs
The control — the rule, gate, or hook that now catches it
The model reports an affordance or a state that does not exist. The tell is always the same: fluent confidence with no referent behind it.
1. The Phantom CommandConfabulatio imperativa
An agent invents a tool, flag, or slash-command that was never real, then recommends it — and, because the recommendation lives in a config file or a hook, it propagates. The system starts citing its own hallucination as fact.
Field marksA /command or CLI flag that "should" exist, referenced with no link to where it's defined.
HabitatHook output, memory files, agent specs, debrief summaries — anywhere the model writes guidance for its future self.
What it costs/respawn — a slash-command I never built — lived across 9 config surfaces for ~4 weeks, re-injected into every prompt by a context-size hook, before I caught it. Four weeks of the system confidently telling me to run a command that did nothing.
The controlSlash-command verification — before any /command is written into a hook, memory, spec, or debrief, it must be verified to exist in the real command set. Recurring misses get a gate, not a louder rule.
2. Context-Drift ConfabulationConfabulatio ex oblivione
Past a certain conversation length, auto-compaction silently drops earlier turns, and the model begins confidently reconstructing state that is no longer in context — reporting work as open that shipped, or duplicating artifacts it already made.
Field marks"Still open" lists that include shipped items; duplicate specs; a turn-count in memory that's higher than the tool's ground truth.
HabitatLong single sessions, especially past ~350K tokens.
What it costsOne past-warning session produced 5 phantom-open items and a duplicate spec before the drift was caught.
The controlTrust context-size warnings, not memory — at the second warning, hand off; verify continuity via the session tool's session_id + prompt count, never from conversation memory; past ~400K, cap-and-close only. Overflow work spawns a fresh linked child session rather than continuing in a drifting context.
3. Orphaned Fan-Out After Parent DeathProles fugitiva
Species recognized, specimen not yet collected. Reported in tooling and harness guidance as a real risk: sub-agents fanned out from a parent that keep running — and keep spending — after the parent dies. I could not find a discrete documented instance of this in my own logs as of this writing, so I list it as a species known from the literature but not yet collected here. It stays flagged until I either find the specimen in the logs or delete the entry. Adjacent, verified cousins live under Genus VI (Orphaned Spawn) and Genus III (Silent Fan-Out).
Genus II — False Success
The most dangerous family. The operation reports done; nothing happened. A green log line is not proof — it is a claim.
4. The Silent No-OpSuccessus falsus
Code runs, exits 0, logs success — and changes nothing. The write was gated behind a stale hash, an argument was dropped, or a splice landed on the wrong line. Everything downstream trusts the green.
Field marks"Added 9 items" with the target unchanged; a generator that "ran clean" over a page that never updated; a nightly job that logs a run but produces no artifact.
HabitatDiff-gated generators, headless CLI scripts, any splice into a shared file.
What it costsA report-card generator whose hash payload only covered three of its fields silently stopped writing a fourth — correct parser, clean run, dead page. A topic-refresh script logged "Added 9 net-new topics" while its splice dropped all 9. A nightly grader logged runs for days while grading nothing, because its prompt argument was empty under a headless invocation.
The controlRead means read — after every write, re-read the target and assert the specific content changed; never say "done" from the log line. For diff-gated writers, the hash must cover every field, computed after the parse. This hardened into a Stop hook that blocks a "done" claim with no verification in the same turn.
An agent claims something was built, persisted, or deployed — and it was not. The subtler variant: a second agent declares the first one's work "unbuilt" without checking, and spins up a whole session to rebuild what already exists.
Field marksA success notification with no artifact behind it; a "this was never built" diagnosis based on a scoped-too-narrow search.
HabitatCloud routines that write to an ephemeral container; parallel sessions that don't check each other's work.
What it costsA scheduled routine "authored" an essay in a throwaway cloud environment, fired a "📖 new read authored" notification, and left nothing in the vault — the notification preceded a persisted artifact that never existed. Separately, a parent session declared a whole research pipeline "built but left unbuilt" — all three scripts existed and worked; the diagnosis came from a search scoped to the wrong directory plus no sibling cross-check.
The controlDefinition of Done — end-to-end before "fixed" — a completion notice must follow a verified persisted artifact (committed + pushed, or live at its URL with HTTP 200), never precede it. Before declaring anything "still open" or "unbuilt," run the parallel-session check and grep the closed-loops files. A child session's "done" is a secondhand report — re-verify it against the artifact, don't relay it.
One action, or one shared dependency, takes out far more than its apparent scope. The failure is not the fault — it's the coupling.
6. The Destructive ResetDeletio lata
A single git operation — checkout, --amend, or an over-broad auto-commit — reaches past its intended target and clobbers uncommitted or shared work.
Field marksHundreds of lines gone after a "revert this one file"; your commit folded under another session's message; a mid-test edit shipped live under an unrelated "chore: refresh snapshot."
HabitatAny repo with uncommitted load-bearing work, and especially any repo multiple sessions or crons write to.
What it costsA mid-session git checkout <file>blew away 483 lines of uncommitted work — recovered only because the file happened to be already pushed to a CDN, so I could curl it back. A git commit --amend in a shared repo rewrote a parallel session's commit. A generator cron's broad git add swept a buggy mid-test file live under a "refresh snapshot" message.
The controlNever git checkout <file> on a vault with uncommitted load-bearing changes — scope edits through the editor or sentinel-safe substitution instead. In shared repos: plain git commit only, never --amend, never reset --hard on shared history — correct a commit with a new follow-up commit. Know which crons auto-commit, and commit intermediate states the moment they're correct.
Many surfaces depend on one shared resource — an API key, a credit balance, a token. When that one thing fails, everything degrades at once, usually silently, and single-surface debugging sends you the wrong way.
Field marksSeveral unrelated features all "showing canned text" or "not refreshing" at the same time; each looks like its own bug.
HabitatA fleet of functions behind one API key; any single-point-of-failure dependency with || true swallowing its errors.
What it costsOne shared API key backs 25+ cloud functions across my personal app; when its balance depletes, every AI surface falls back to canned content simultaneously — and they all used to fail silently, the quota error swallowed. Diagnosing any one of them in isolation is misleading.
The controlNever swallow the error — wrap every shared-dependency call in try/catch and log the real error into the response payload, never || true. On any "nothing's generating fresh content," check the shared balance first, before the endpoint's own logic. Pick a degraded-state fallback that stays useful, not one that looks broken.
Real data exists, and the system cannot see it. The reader is format-blind, or the audit silences itself. The system reports green while reality drifts.
8. The Format-Blind ReaderLector caecus
A parser recognizes one exact format. Data in a reasonable-looking variant is invisible to it — present in the file, absent from every count and dashboard that reads through the parser.
Field marksThe same list showing wildly different counts across surfaces; items you know you wrote that no view displays.
HabitatAny hand-maintained file read by a strict parser — checklists, logs, front-matter.
What it costsMy loop parser only recognizes one bold checkbox syntax. Items written in ordinary - [ ] markdown were invisible to it and to the loop-closer both. An audit found 44 such items silently uncounted — one file held 96 open items while displayed counts ranged from 15 to 55 across surfaces. Standard markdown looked correct and broke the whole ecosystem.
The controlA documented canonical format that every writer obeys, enforced at the point of authorship; when a reader is strict, the writer must be too. The audit that found it is now a standing net.
The watchdog meant to catch drift is itself the thing that hides it. Hash-suppression, item caps, and namespace pre-filters make a growing problem look like "no new drift," and the once-a-day ping habituates into silence.
Field marks"No drift detected" while you can see drift with your own eyes; a queued fix that's been sitting unread for weeks; a head -5 in the audit body and its hash.
HabitatDrift detectors, representation audits, any idempotent job that suppresses duplicate alerts.
What it costsA representation audit correctly queued a fix with 5 items — 4 more were silently capped out, never entered the spec, never invalidated the suppression hash, and the whole thing sat unread for weeks while the system reported green.
The controlAn age escape-valve — re-fire and escalate tone if a finding persists past N days even when the hash is unchanged (🔴 STALE QUEUE at >7 days). Enumerate the full set on a manual dry-run before trusting any "no drift." Exclude noise with a post-filter, never an inclusion-regex that drops everything else.
It looks like the thing. It is a pointer to the thing, or a stale echo of it. Acting on the resemblance is the failure.
10. The Sync-Stub TrapSimulacrum syncs
A cloud-synced file on disk is not the file — it's a tiny stub pointing at the real object in the cloud. Treat it like a normal file (move it, delete it, "clean it up") and you damage the real thing or orphan a live document.
Field marksA .gdoc / .gsheet / .gslides that's a few hundred bytes; a "loose file at the vault root" that's actually a Drive stub.
HabitatAny Drive-for-Desktop or cloud-synced folder.
What it costsThis one is a trap I hold as a hard rule specifically so it never posts a bill — never delete or move a cloud-office stub from the filesystem; the sync stubs at the vault root are never loose items to file. It's in the guide as a control that predates its own incident on purpose.
The controlHard rule — cloud-office stubs are never deleted or relocated from disk; batch file operations dry-run and confirm before touching anything that could be a stub.
SpecimensHeld as a preventive control — no incident on record, by design.
11. The Cache MirageImago vetusta
The server is correct. The browser is lying — replaying a stale cached response — and you burn hours "fixing" a gate that already works, redeploying a correct server against a client-side ghost.
Field markscurl returns the right thing; the browser shows something else; every redeploy "fixes" it and nothing visibly changes from the user's seat.
What it costsA "nothing should be public" gate looked broken in the browser across ~5 deploys; one debugging session degraded to ~600K tokens chasing it. Root cause: an earlier commit had briefly served a cacheable public response, and a reused incognito session kept replaying that stale entry. The server was fine the entire time.
The controlProve the server with curl (full browser UA, unique cache-id per hit, no-store), then prove the browser with a cookieless headless render — the whole bug is browser-vs-curl divergence, so curl alone can't see it. If both pass, stop editing the server; the fix is client-side.
Many sessions, many spawns, one substrate. Failures here are about who was writing when, and what outlived what.
12. The Cross-Session CollisionConcursus sessionum
Multiple autonomous sessions and crons write the same repo or file. Between one session's read and its write, another lands a change — commits get rewritten, edits get swept, pushes fail on a branch topology nobody chose.
Field marksYour commit under someone else's message; a nightly push that fails every night with "no upstream"; two sessions converging on one script.
HabitatA shared vault with parallel sessions + a nightly commit cron + hourly generator crons.
What it costsThe nightly commit cron sat stuck on an un-merged feature branch with no upstream — pushing failed every night, masquerading as a credit error while 55 commits piled up locally. On a busy autonomous night, sibling sessions had to be given disjoint directory ownership to keep two of them off the same script at once. (The ownership assignment is documented; whether a clobber actually occurred that night or was purely prevented, I don't claim — the guardrail is the point.)
The controlParallel-session check before declaring state or writing shared files; keep the vault on main so the nightly push tracks; assign disjoint working directories to concurrent autonomous sessions; plain-commit-only discipline (see Destructive Reset).
A spawn setting meant for one case silently propagates into the defaults, and every child session inherits it — severing the link that was supposed to hold the work together.
Field marksOverflow child sessions that don't appear alongside their parent; a flag copied into three config files that "everyone" now uses.
HabitatSession-spawn configuration, model-routing hooks, memory that documents defaults.
What it costsAn isolated: true flag — correct for genuinely independent work — silently propagated into the default rules, a routing hook, and a memory file, orphaning every overflow spawn from its parent's workstream. The opposite of what same-task overflow needs.
The controlSpawn linked by default — children share the parent's workstream unless a specific case needs isolation; a soft gate warns when isolated: true is passed. Defaults are audited for silently-propagated flags.
Not that my stack is unusually broken. Every one of these is a generic failure class — you will meet them in any agentic system at this scale. The claim is narrower and, I think, more useful:
I can name them, date them, price them, and show you the specific control that now catches each one.
That is the difference between someone who has read about agent failures and someone who has paid for them. The taxonomy is the receipt.
Read the dated specimens in /incidents. Read the controls they promoted in /governance.