I fanned a design-token sweep out to parallel Claude Code subagents: roughly 317 hardcoded hex colors scattered across an app’s screens and components, all to be replaced with tokens from a central theme file. Each agent got a slice of files and reported back when done. The reports came in clean. The grep afterward disagreed. Some slices were untouched, and the agents responsible had said nothing, because they no longer existed to say anything.
That sweep, and the surrounding sessions, taught me that a subagent’s self-report can be wrong in both directions. Agents that failed report nothing or claim success. Agents that succeeded report catastrophic failure. If your orchestration trusts the narration, you will redo finished work and ship unfinished work, sometimes in the same run.
Four failure modes, each with the specific lie it tells.
1. The silent death
An agent hits a session limit (or any hard kill) mid-task and stops. No error reaches the orchestrator, and there is no partial-work report. The run moves on, and the only evidence is the absence of changes in files nobody rechecked.
The lie: no news reads as good news. An orchestrator that treats “agent finished without complaint” as “agent finished” inherits every one of these deaths as a silent gap in coverage.
The fix is mechanical: re-run the sweep’s search pattern over each agent’s assigned files after it reports. Not a spot check of the agent’s summary, but the actual pattern over the actual scope, run by the orchestrator itself. In my sweep this is what turned “all done” into a list of leftovers to fix myself.
2. The false confession
One verifier agent ran 48 tool uses over about 400 seconds, wrote its report file successfully, and then died with API Error: Internal server error. The crash happened during trailing work that didn’t matter: saving memory entries after the deliverable was already on disk.
From the outside this looks like a total loss. It was the opposite: the work was complete and the failure was cosmetic.
The lie: a crash status on top of finished work. Believe it and you re-dispatch a long agent whose output is already sitting there, paying the full cost twice. Before re-running any crashed agent, ls and read its expected artifact. If the file exists and passes a structural check, spot-check two or three of its claims and keep it.
The prevention is prompt-side: tell long-running agents to write the deliverable before any optional trailing work. A late crash then costs nothing.
3. The zero-work zombie
A 529 Overloaded can kill an agent before it does anything at all. The result still reports a runtime of several minutes in duration_ms, which is exactly what makes it convincing. The giveaway is elsewhere: total_tokens: 0, tool_uses: 0.
The lie: duration implies effort. Minutes of wall-clock read as minutes of work, and the natural recovery is “continue the agent from where it stopped.” But there is nowhere. At zero tokens there is no context to continue; the continue-style recovery hint in the crash message is a dead end. Re-dispatching a fresh agent with the same prompt worked on the first try.
Related trap: a top-level 529 arriving right after a Write does not mean the Write failed. Check the file before redoing anything.
4. The inline bypass
The strangest one: an agent produces the deliverable in full, as chat text, and never calls Write. The tool is in its list. The prompt named the output path. The content is right there in the final message, and the file does not exist, so every downstream stage that reads the path gets nothing.
The lie: the work visibly exists, just not where the pipeline needs it. It is tempting to accept the chat text and write the file yourself on the agent’s behalf. Don’t. The moment the orchestrator starts transcribing for agents, you’ve built a manual step into an automated pipeline and lost the signal about which agents actually comply.
What ended it for me was a write-or-block contract in every agent prompt: a mandatory Write call to the provided path, an explicit ban on printing the deliverable in chat, and a final message restricted to “wrote <path>“. Compliance became checkable in one line.
The gate that catches all four
Since the sweep, every dispatch ends with the same cheap Bash gate, run by the orchestrator before anything downstream:
[ -f blocker.md ] && echo BLOCKER
[ -s report.md ] && echo OK || echo MISSING/EMPTY
grep -c "^## Findings" report.md
Enter fullscreen mode Exit fullscreen mode
Existence, non-empty, structural shape, all in one call, without loading the artifact into context. This gate caught both a 529-dead executer and an inline-bypass reviewer with zero ambiguity: it printed MISSING/EMPTY and left nothing to interpret. Paired with the grep-over-assigned-scope check from failure mode 1, it covers everything above in both directions, dead agents that claimed nothing and live transcripts that claimed too much.
The takeaway
I wrote before about orchestrators silently improvising when a tool goes missing, and the conclusion here is the same one, widened: narration is not evidence. An agent’s final message, its crash status, even its runtime duration are all just signals about the work, and every one of them can be wrong. The artifact on disk is the work. Gate on that, grep the scope, and treat everything an agent says about itself as a hypothesis to verify at a cost of one Bash call.
Trust the filesystem. It has never once lied to me.
Failure modes collected from real Claude Code multi-agent runs, including a sweep, split across parallel subagents, that replaced ~317 hardcoded hex colors with central design tokens. The write-or-block contract and artifact gates now ship in Suhail, the orchestrator I use daily against production Expo/Supabase repos. Error payloads (API Error: Internal server error, 529 Overloaded, total_tokens: 0) are from actual session results, July 2026.
답글 남기기