Deadlock, Livelock, and the Agent That Waits Forever

A deadlocked thread pool gives you a stack trace. Deadlocked agents produce fluent, plausible activity that looks like work — and bill you for it until someone notices.

Multi-agent systems inherit every coordination failure from distributed systems, plus a few of their own. The difference is that a deadlocked thread pool announces itself with a stack trace, while deadlocked agents produce fluent, plausible activity that looks like work.

Four failures, each with a structural fix.

Deadlock: everyone is waiting

Agent A delegates to B and waits. B needs something only A can provide and asks. If A's implementation blocks on B's result, neither proceeds.

The agent version is subtler than the thread version because the "lock" is often implicit — a shared resource, a file, an approval, a queue item claimed and never released.

Fix: questions travel only up the hierarchy, never sideways or back down. An agent waiting on a subordinate must be able to receive a question from it as a result, not as a blocking call. Combined with a strict tree topology — no cycles in the delegation graph — deadlock becomes structurally impossible rather than merely unlikely.

Livelock: everyone is busy, nothing progresses

The more common and more expensive failure. Two agents pass a task back and forth, each concluding it belongs to the other. Or a reviewer and a writer cycle: revise, reject, revise, reject, with the objection restated in new words each round.

Nothing is stuck. Turns are consumed, tokens are spent, the transcript looks like diligent collaboration. Without a specific check, this runs until a budget stops it — and if there's no budget, it runs until someone notices the bill.

Fix: detect repetition explicitly. Hash each state — the task plus the current artifact plus the agent holding it. If a hash recurs, the system is cycling. That's a five-line check, and it catches what turn limits catch only much later and much more expensively.

Also: every handoff must record a reason that hasn't been used before. A rejection repeating a prior objection means no new information entered, and the cycle should be broken rather than continued.

The orphaned wait

A worker is dispatched and never returns — crashed, timed out, or waiting on something that will never arrive. The supervisor waits indefinitely, holding its context, its budget, and possibly a lock.

Fix: every delegation carries a deadline and a defined behavior on expiry. Proceed without that result, retry once, or escalate — but decided in advance, in code. ⚠️ The absence of a timeout on delegation is one of the most common structural defects in multi-agent systems, because it never shows up in testing where everything responds.

Convoy: everyone waiting on the same thing

Ten workers fan out, and all ten need the same rate-limited API. Nine wait while one proceeds. Throughput is one worker's worth, cost is ten workers' contexts held open.

Fix: a shared concurrency limit at the tool layer, sized to the actual constraint, plus — better — a cache. Ten workers requesting the same reference data should produce one call and nine cache hits. Convoys usually indicate that the fan-out dimension was chosen wrongly: work split by document but bottlenecked on one shared lookup should have fetched the lookup once, before fanning out.

🔍 Detecting all four

Three instruments, none expensive:

A run-level DAG. Record every delegation as an edge. Cycles are visible immediately, and so are the nodes with no returning edge — those are your orphans.

Progress, not activity. Track a definition of forward progress specific to the task: items completed, artifacts changed, findings added. An agent system consuming turns with flat progress is livelocked, and this is the single most useful alarm in a multi-agent deployment.

Wait-time attribution. For each agent, time spent computing versus waiting. High wait across many agents at once is a convoy; high wait for one is an orphan.

✅ The structural defaults

Most of this is avoidable by construction rather than detection:

  • Strict tree topology; no cycles in delegation.
  • Every delegation has a deadline and an expiry behavior.
  • Questions go up only.
  • Global budget covering the whole tree, not per-agent — otherwise a fleet of individually-well-behaved agents can still collectively burn everything.
  • State-hash repetition detection on any loop involving more than one agent.
  • Shared resources behind a cache and a concurrency cap.

The takeaway

Agent coordination failures look like productivity, which is what makes them expensive. Enforce a tree with deadlines on every delegation, let questions travel only upward, budget the whole tree rather than each participant, and measure progress rather than activity. Then a livelock shows up as a flat line within a minute or two, instead of as an invoice at the end of the month.

Keep reading

Similar posts

Matched on shared tags and category — the more bars, the stronger the overlap with what you just read.