Subagents Are Zombie Processes

vicvelazquez1 pts0 comments

Your subagents are zombie processes<br>Jul 6, 2026<br>Your subagents are zombie processes<br>#agents#orchestration#multi-agent#claude-code

I found it because the cursor was blinking.

The review was done — posted, merged, forgotten. But the session was still flickering, so I opened the background-tasks panel, and there they were: two agents, still running, five and a half hours after they had anything to do. Around a hundred thousand tokens, spent re-deriving an answer I’d already shipped. A week earlier, on another project, a single agent had spawned 106 subagents for one web search. I find these the same way every time: by accident.

If you’re building anything multi-agent, you have these too. You just haven’t caught them yet.

Nobody owned the children

Here’s the mechanics, because they’re mundane and that’s the point.

I asked an agent to review a big pull request by fanning out — a few subagents, each on a different dimension. Good pattern; it caught a real bug. One of those subagents parallelized its slice and spawned two of its own. Then the parent finished, reported up, and returned — without stopping the children it had started. It never treated their lifecycle as its job.

So they orphaned. Running, billing, invisible, one level below anyone who was watching. This wasn’t a bug in any single agent’s reasoning. Every agent in that tree did its job well. It was a gap in ownership — nobody was responsible for turning the children off.

There was no switch

My first instinct was to find the setting. There isn’t one, and the search is worth sharing, because the absence is the story:

Lifecycle hooks exist, but they’re the wrong shape. They fire when an agent finishes and hand you its final message. They can’t enumerate a sibling that’s still alive, and can’t reach over to kill it. They react; they don’t reap.

There’s no max-runtime or auto-kill for subagents. Nothing times out a hung child.

The platform has open bugs in exactly this spot — finished subagents that never get reconciled and linger in a running state. So part of what I hit wasn’t my orchestration at all. The infrastructure has no reaper either.

That’s the uncomfortable takeaway for the whole field: we’re wiring up systems that spawn processes, and we haven’t built the process table.

We solved this in 1985

An orphaned subagent is a zombie process . The vocabulary maps one-to-one:

A child outlives its parent’s attention → orphan .

Nobody collects its exit status → zombie .

No supervisor adopts and cleans it up → no init/reaper .

Unix answered this decades ago with two ideas: a parent is expected to wait() on its children, and if it doesn’t, init adopts the orphans and reaps them. Multi-agent frameworks today reliably have neither. We skipped the lesson because “agent” sounded novel enough to feel exempt from operating-systems history. It isn’t. It’s fork() with a language model attached and a credit card.

And the agent case is worse than the Unix one in three ways that matter:

The leak is silent. A zombie process shows up in ps. An orphaned agent shows up as a bigger invoice next month.

The resource is money. Idle isn’t free — a hung agent can keep thinking, and thinking bills.

There’s no default supervisor. Your terminal reaps your child processes when it dies. Nothing reaps your agents.

The fix is the old fix: ownership, delegated recursively

You don’t need the platform to grow a reaper. You need the thing Unix already assumes: every parent owns its children, because the parent is the one holding their handles.

That last clause is the whole insight. When I couldn’t kill the orphans, it was only because they were my grandchildren, one level below me. But their direct parent did hold their IDs. It could have stopped them. It just was never told that was its job.

So make it the job, and make the instruction travel. Every spawned agent carries a clause like this, with orders to pass it down to anything it spawns:

Lifecycle contract (include verbatim in any subagent you spawn): Do your task. Before you return, make sure any subagents or jobs you started have finished or been stopped — you hold their handles, so stop any still running. Don’t return while leaving work alive; if a child is stuck, stop it rather than waiting on it. If you spawn helpers, pass them this same paragraph so the rule keeps propagating.

Because it propagates, it self-assembles into a teardown tree. Level four cleans five, three cleans four, the root cleans the top. No depth cap, no width cap — fan out as hard as the task deserves. The only invariant is that nothing returns while its own subtree is still alive.

Here’s the failure and the fix side by side:

WITHOUT the contract — the ghost WITH the contract — clean teardown<br>──────────────────────────────── ──────────────────────────────────<br>you ✓ done, moved on you ✓ + sweep: verify all clear<br>└─ A ✓ returned findings └─ A ✓ stops B before returning<br>└─ B ✓ returned findings └─ B ✓ stops C, D before returning<br>├─ C...

agent subagents because zombie parent processes

Related Articles