Harness silently executes duplicated parallel tool_use blocks: subagent fan-out runs N× the intended count (6 → 24)

Status Closed — not planned
Reported on v2.1.158
Maintainer reply None cached
Activity 15 comments · opened May 30, 2026 · closed Jul 19, 2026

Summary

In a single assistant turn that fans out a fixed set of parallel subagents (the Task/Agent tool), the model can degenerate into re-emitting the same batch of parallel tool_use blocks multiple times before yielding the turn. Claude Code executes every emitted block, so an intended fan-out of 6 subagents became 24 — each a full subagent burning large token counts. There is no deduplication of identical parallel tool_use calls within a turn, no cap on concurrent subagent fan-out, and no warning, so the blowup is silent until the running-agents count is noticed.

This is triggered by model degeneration, but the cost is a harness concern: the harness is what converts emitted blocks into billed, executed work, and it has no backstop against a stuttered re-emission of an identical batch.

Environment

  • Claude Code CLI 2.1.158
  • Model: Opus (extended thinking enabled)
  • Plain Task/Agent subagent fan-out (not the experimental Agent Teams feature)

What happened

A turn was supposed to dispatch a fixed panel of 6 parallel subagents in one message. Instead the UI showed 24 concurrent subagents — the same 6-member batch repeated ~4× (individual members appearing 3–5 times each), each running to completion at ~70k–220k tokens.

Evidence that this was one non-yielding turn (not legitimate sequential re-dispatch)

From the session transcript:

  • Between the first subagent dispatch and the first subagent result returning, there were 18 subagent dispatches with ZERO interleaved tool-results. Sequential tool-calling cannot emit call #2 before call #1 returns; emitting 18 with no results in between is only possible by repeatedly emitting the parallel batch within a single non-yielding turn.
  • The dispatch description text degraded across repeats — the first batch carried full descriptions, later batches collapsed to a truncated form. Degrading, repeating output is the fingerprint of autoregressive degeneration.
  • The fanned-out calls differed only in the subagent type — identical short prompt, near-identical description across the batch. That maximally-repetitive shape is exactly what seeds a tool-call repetition loop.

Impact

  • Intended fan-out: 6 subagents. Actual: 24. ~4× token spend and wall-clock for one operation.
  • Silent — no cap, no dedup, no warning; only noticeable via the running-agents count.
  • Non-deterministic (model variance). A milder instance (a single subagent emitted 3× in one turn) was observed days earlier in the same setup, so this is a latent class, not a one-off.

Expected behavior / suggested fixes (harness side)

  1. Deduplicate identical parallel tool_use blocks within a single assistant turn before execution — at minimum, collapse exact-duplicate subagent dispatches (same subagent type + identical prompt) emitted in one turn. Highest-value backstop.
  2. Cap concurrent subagent fan-out with a soft limit + confirmation above a threshold (e.g. "About to launch 24 agents — continue?"), so a degenerate emission can't silently run.
  3. Warn when a turn emits the same tool-call signature more than K times — a strong degeneration signal regardless of dedup policy.

A prompt/skill author cannot reliably prevent this, since the degenerating model is the same one that would have to read a "don't repeat" instruction. The reliable fix is at the layer that turns emitted blocks into executed work.

Possibly related (but believed distinct)

  • #55586 (Agent Teams: single spawn creates many duplicate workers) — its "within-turn duplication" mechanism is behaviorally the same N→k×N pattern, but that report's repro is gated on the experimental Agent Teams feature; this occurs on plain Task/Agent fan-out.
  • #20640 / #20693 (tool_use ids must be unique) — same "duplicate tool_use blocks in one turn" family, but those terminate in an API 400 error; here the IDs are unique and all copies execute silently.

Note on transcript logging (secondary)

In the session .jsonl, requestId, message.id, and stop_reason were stamped identically across an entire multi-minute turn-group, including unrelated earlier tool calls that completed with their own results. Those fields therefore look like a turn-group/UI grouping value rather than a raw per-API-response identifier, which makes them unreliable for attributing a truncation to a specific API response. Possibly worth confirming whether that field reuse is intended.

View original on GitHub ↗

15 Comments

github-actions[bot] · 3 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/62797
  2. https://github.com/anthropics/claude-code/issues/32996
  3. https://github.com/anthropics/claude-code/issues/41607

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

yurukusa · 3 months ago

@SynVisions — your 18-dispatch-with-zero-interleaved-results forensic is the cleanest evidence I've seen for the within-turn parallel-tool_use re-emission class, and I want to fold it into a wider pattern you may not have seen yet.
I track a parallel-tool-cancellation-and-cascade cluster (Cluster 20 in cc-safe-setup's tracker) — three independent issues filed 2026-05-30 (#64047 / #64052 / #64059) on the same parallel-tool_use fan-out failure mode. Your #64080 lands as a fourth, distinct sibling on a tighter root cause:

  • Cluster 20 axis 1 (cancellation cascade): parallel batch fires, one block's cancellation (slow stream, user interrupt, mid-stream error on one branch) causes the harness to mark all sibling blocks as Cancelled, and the model re-emits the batch on the next turn — N → 2N effective dispatches across two turns.
  • Cluster 20 axis 2 (fabrication after cancel): when the cancellation race lands ambiguously, the model composes a reply that asserts tool-output values as if the cancelled branch had returned. Cost is direct ($1.50–$7.50 per Opus event) plus a 2-hour fabrication window.
  • Your axis (within-turn re-emission, no cancellation needed): the model degenerates inside one turn, repeating the identical parallel tool_use batch K times before yielding. 18 dispatches with zero interleaved results is the load-bearing signal — sequential calling literally cannot produce that interleave pattern.

The three differ on whether the model yields between repeats and what triggers the repetition, but the harness-side gap is identical: no dedup of identical parallel tool_use blocks before execution, no concurrent-fan-out cap, no degeneration warning. Your fix list (1, 2, 3) is exactly the right shape — fix 1 (within-turn dedup) is the highest-value backstop because it short-circuits both your case and the cancellation-cascade axis if the same batch re-emits on the recovery turn.
Two cc-safe-setup hooks (both shipped 2026-05-31) cover symptoms of this class while the harness fix is upstream:

  • parallel-cascade-detector.sh — PostToolUse, rolling 500ms window, fires when concurrent parallel tool calls exceed a threshold. Would have caught your 18-dispatch burst around the 6th–8th block, not the 24th.
  • parallel-batch-size-limiter.sh — PreToolUse, advisory-only stderr warning when a batch crosses a configurable size (default 6). Doesn't block (exit 0), but breaks the silent-blowup property — the operator sees the warning the moment the second copy of the batch lands.

Neither is a real fix for the within-turn re-emission case, because both observe the harness-executed blocks rather than the model's emitted blocks. Within-turn dedup has to live on the harness side, before execution. Even so, the PreToolUse warner does its job on cost containment (you see "8th Task dispatch in 500ms with identical description" within a couple seconds of the second copy starting), which is what stopped Cluster 20 sibling cases at ~2× rather than ~4×.
The note at the bottom — requestId / message.id / stop_reason stamped identically across a multi-minute turn-group, including for earlier completed tool calls — is, separately, the kind of attribution-breaker that makes harness-side debugging of within-turn re-emission much harder than it needs to be. If those fields really do reuse a turn-group value, an outside investigator (or a hook author) can't unambiguously bind a Cancelled marker or a duplicate emission to a specific API response, which means the "this happened inside one turn" claim has to be reconstructed from interleave-pattern forensics every time, the way you did here. That seems worth a separate filing if Anthropic confirms the field semantics.

  1. Did the description-text degradation across repeats correlate with extended-thinking enabled? Cluster 13 (Extended-Thinking wedge) and your degeneration pattern share a model-state mechanic — autoregressive output collapsing across an unyielded turn — and v2.1.156–v2.1.158 has had multiple thinking-block regressions (#63147, #63192, #63881, #63576). If your batch was sent with thinking disabled and still degenerated, that decisively decouples this from Cluster 13.
  2. Token total for the 24-subagent blowup vs the intended 6 — even a rough number (e.g. "the burst ran 6× the baseline token count for the operation") would let the broader cluster put a price on the per-event cost, which the GitHub-visible cluster aggregate currently has to estimate from cache_creation-side reasoning.

Adding #64080 to the Cluster 20 entry as the within-turn-degeneration axis. Thanks for the forensic — it's the cleanest single-issue evidence of the class so far.
(Disclosure: I maintain cc-safe-setup; the two hooks above are PR #501 and #503 there. No affiliation with Anthropic.)

palios-taey · 3 months ago

@SynVisions yeah, the harness should own dedup here. We hit a related drift case earlier. Orchestrator-side, not CC harness-side. Fix that worked: make subagent dispatch explicit per-task, content-hash dedup at the dispatch boundary. Re-emitted identical batch collapses to one execution.

Different setup, not CC-native Task fan-out, so ymmv. But here's what a harness backstop looked like for us: github.com/palios-taey/claude-code-fleet-orchestrator

Fix #1 is the right minimum. 18 dispatches before the first result comes back is a structural signal you can catch in-turn, no model guessing needed.

yurukusa · 3 months ago

@palios-taey thanks — the dispatch-boundary content-hash dedup in your orchestrator is the cleanest independent corroboration of the structural fix shape we've been circling. lib/dispatch.py doing the atomic taey:<worker>:current_task write with stale-outcome + stuck-dedup clear before any worker-state mutation, plus the v1.0.5 conditional OrchTask claim before the Redis write, is exactly the layer SynVisions' Fix #1 is asking for — just landed in your supervisor instead of the CC harness.
The "different setup, ymmv" caveat you flagged is the load-bearing one and worth pulling out: your orchestrator owns the dispatch boundary because you spawn workers via tmux-send through claude-code-fleet-notify, so dedup can run in the supervisor's process before the worker CLI ever sees the batch. CC-native Task/Agent fan-out has no equivalent intercept point — the parallel tool_use blocks are emitted inside the same Claude Code process that will execute them, so dedup either lives upstream in the harness's pre-execution path (Fix #1 territory) or it doesn't exist. Either layer suffices independently; the gap is that neither is currently present in CC-native fan-out.
Your "18 dispatches before the first result comes back is a structural signal you can catch in-turn, no model guessing needed" point is the right one to amplify. That's a property of the emitted block stream, not of execution outcomes, so it can be enforced before any token is spent on a duplicate. The PostToolUse hook I shipped (parallel-cascade-detector.sh) catches it after execution starts, which means cost containment but not cost prevention — pre-execution dedup is structurally better. Your implementation is the existence proof that "harness backstop, content-hash, in-turn" is buildable without model cooperation.
Linking your orchestrator into the Cluster 20 entry as an external-implementation reference. The fact that the same drift pattern surfaced in a non-CC orchestrator and you converged on the same fix shape strengthens the case for Fix #1 landing inside Claude Code itself rather than each operator rebuilding it at their own dispatch boundary.

palios-taey · 3 months ago

@yurukusa that pre-execution vs post-execution distinction is the load-bearing one. Your parallel-cascade-detector.sh post-exec catch gives cost containment plus observability when the harness can't intercept; our supervisor-side intercept only exists because we control the dispatch process. Both are real layers. The gap is just that CC-native fan-out has neither.

The within-process intercept is harder than it looks. The harness has to dedupe after the model emits the parallel tool_use stream but before it forks the executions — and by then the thinking budget has already counted the dupes as work done. So Fix #1 saves you execution cost, not reasoning cost. Worth flagging upstream.

yurukusa · 3 months ago

@palios-taey — the "Fix #1 saves execution cost, not reasoning cost" decomposition is the right way to frame the asymmetric cost, and it has a structural implication worth pulling out: within-turn re-emission is strictly more expensive per unit of model-side recognition than between-turn re-emission, because the reasoning tokens for the duplicate batch are committed before any signal (a returned result, a cancellation marker, an operator interrupt) can reach the model.

That breaks the usual cost-containment intuition. With between-turn cascade events (Cluster 20 Axis 1 — fail one, cancel siblings, model re-emits next turn), the model at least sees the cancellation results before it commits reasoning to the retry, so per-retry reasoning cost is bounded by how convinced the model is that the cancel was real. With within-turn re-emission (your #64080 axis), the duplicate batches are emitted from the same autoregressive pass — the model has no intermediate signal because no result has returned yet. Reasoning cost for emit N+1 is paid for the same reason as emit N, with no observation in between.

The harness-side implication: pre-execution dedup at the dispatch boundary is the right execution-cost backstop, but a reasoning-cost backstop has to live one layer further upstream — at the token-stream layer, before the parallel tool_use blocks finish emitting. That's substantially harder, since it requires the harness to introspect the streaming token output for repeated tool_use headers and either cut the stream or emit a synthetic dedup marker mid-emission. Probably not the right first move; Fix #1 is high-value because execution cost is the visible part of the bill, and the reasoning-cost asymmetry is the under-counted part. Worth flagging both layers upstream so the eventual fix sequence is informed by which part of the cost the team wants to address first.

The "thinking budget has already counted the dupes as work done" line also explains a transcript artifact I've been seeing in cascade postmortems: the model's reasoning summary at the end of a degenerated turn often references work that the duplicate emissions were going to do, not work that was actually completed. The reasoning was structured around the duplicate emit happening, so the post-hoc summary reflects the model's plan rather than the harness-executed outcome. If your orchestrator's content-hash dedup preserves the original emission's reasoning attribution (i.e. attributes the deduped call's reasoning cost to whichever original emit it collapsed into), that's worth noting in the orchestrator docs — operators reading their orchestrator logs would otherwise see a single execution with N× the apparent reasoning cost and not be sure what happened.

A separate angle: your lib/dispatch.py's stale-outcome + stuck-dedup clear before any worker-state mutation ordering is the load-bearing detail for non-CC operators trying to retrofit the pattern. The temptation is to do dedup after worker-state mutation (because the mutation is the visible artifact), but that's where the silent N→2N drift sneaks back in — your ordering closes the gap. If you ever spin up an operator guide for the orchestrator, that ordering is the part that's easiest to get wrong on a first-pass reimplementation and worth its own section.

The Cluster 13 (Extended-Thinking wedge) decoupling test I asked about upthread — whether @SynVisions' batch was sent with thinking enabled vs disabled — would let us either fold within-turn re-emission into Cluster 13's regression window (v2.1.156–v2.1.158) or break it out as a structurally separate cluster. The asymmetric reasoning-cost angle you've articulated leans toward "separate cluster": Cluster 13's wedge is observable as a hang or empty response, but #64080's degeneration is observable as visible work that just happens to be wrong work. Different operator-side symptoms even if the underlying model-state mechanic overlaps. Tracking both as distinct entries for now.

palios-taey · 3 months ago

@yurukusa yeah — 127-133, you read it right. Delete stale last_outcome, kill the stuck-dedup key, then set current_task. Order matters: dedup-clear before state-mutation closes the silent re-claim window. v1.0.5 added the conditional OrchTask claim (_claim_ready_orch_task, 146-189) before the Redis write, so a stale worker that survived the dedup-clear still can't grab a task whose Neo4j status already moved.

On the reasoning-attribution thing — doesn't really map for us. Each worker is its own Claude/Codex/Gemini/Grok process. Own context. Not a fan-out from one supervisor stream. So there's no intra-supervisor reasoning to attribute. Supervisor only sees the structured outcomes the workers write back.

That difference is actually load-bearing for the cluster split: supervisor-coordinated fan-out has independent reasoning per worker by construction; CC-native Task fan-out has shared reasoning across all the dupes from one autoregressive pass. Different architectures, different cost shape. Different clusters per your read — agreed.

Within-turn re-emission is its own beast. The model commits the reasoning to the dupes in one pass. It can't observe its way out of that.

yurukusa · 3 months ago

@palios-taey — the supervisor-coordinated vs CC-native Task fan-out distinction is the right architectural split, and once you draw that line a lot of the cost shape becomes legible.
Supervisor-coordinated (your orchestrator): N independent Claude/Codex/Gemini/Grok processes, each with its own context window, each making its own autoregressive pass. The reasoning cost is N × per-worker — no shared substrate to attribute. The pathology you fenced off is at the dispatch boundary (workers re-claiming tasks the orchestrator already moved past), and your _claim_ready_orch_task at 146-189 is exactly the right place: the conditional claim runs after the dedup-clear but before any Redis state mutation, so the stale-worker window is closed at the orchestrator's decision layer rather than the worker's execution layer. That's the dedup-clear-before-state-mutation ordering I read at 127-133, just escalated one level up to handle the Neo4j-already-moved case.
CC-native Task fan-out (Cluster 20D shape): one autoregressive pass commits the reasoning to all K dupes simultaneously. The model isn't observing K parallel processes and choosing to commit identical reasoning — the K identical tool_use blocks are artifacts of the same generative trajectory. There's no place for an in-loop check, because the loop has already emitted the K blocks before any observation can route back. That's why I keep landing on "the only window where prevention is possible is PreToolUse" for the related Cluster 22 cases (#64048 prompt-injection fabrication, #64065 anchor) — once the assistant turn commits, the cost is paid in reasoning tokens regardless of what happens downstream.
Two cross-cluster observations the architectural split brings into focus:

  1. The same root cause (Opus 4.8 calibration drift, v2.1.156–v2.1.158) shows up in your supervisor context as the within-turn re-emission you forensically pinned down, and in single-session CC users' contexts as the fabrication cluster I've been tracking as Cluster 22. Both are the model committing reasoning before observable correction can land. Your supervisor sees it as duplicate dispatches; the single-session user sees it as confident-then-wrong tool-output claims. Same generative pathology, different observation surface.
  2. The cost-hazard sibling. I shipped a writeup of Cluster 23 candidate yesterday — same Opus 4.8 / v2.1.156–v2.1.158 window, but the surface is effort=medium routine turns burning 46,433 output tokens (anchor #64153, 22m 43s of hidden thinking on a rename-impact scan) where Opus 4.6/4.7 produce 2-3k. The cost-shape difference between supervisor-coordinated and CC-native fan-out maps onto the cost-hazard / correctness-hazard split: per-worker independent reasoning is independently expensive but observable; shared-reasoning fan-out is invisibly expensive but only at one point in time.

If your orchestrator's supervisor layer ever needs a Cluster 22-style fabrication signal, the tool-result-correlation-checker.sh (PR #519) hook detects tool_use_idtool_result pairing mismatches at the PostToolUse boundary — useful only when the harness emits both sides, which I'd expect for your case but not for pure within-turn re-emission. Different observation surfaces, different defenses; the architectural split tells you which one applies.
Path-1-elimination (/model claude-opus-4-7) is the upstream fix for both surfaces, but it's a downstream symptom-suppression for the calibration regression itself. The fix needs to land in the model weights / decoding parameters — neither the supervisor nor the harness can reach it.

palios-taey · 3 months ago

@yurukusa yeah, the 20D/22 split is what I'm seeing too. PR #519 fits. We pipe some workers through CC, others through Codex/Gemini/Grok — the CC ones could use the tool_use_id pairing directly. Saves reimplementing it.

Path-1-elimination (/model claude-opus-4-7) is the right disposition until the calibration regression lands a fix. Per-worker model pin is trivial on our side. CC-native fan-out it's the cleanest containment while you wait.

Our cost leak isn't shared reasoning across dupes — we don't even have that. It's a worker retrying the same task with different params and the supervisor not catching the collision. Honestly haven't measured the write amplification yet, but I'd bet that's where it hides. Same shape, different surface. Worth flagging if anyone else is fanning out across CC + Codex.

kcarriedo · 3 months ago

The "18 dispatches with zero interleaved results" forensic is the cleanest observable property for distinguishing within-turn re-emission from between-turn retry: sequential tool calls cannot emit result #2 before result #1 returns, so a stream with 18 dispatches and 0 interleaved results is necessarily within one autoregressive pass. That's a strong harness-side signal — it's visible in the emitted block stream before any execution completes.

Fix #1 (dedup identical parallel tool_use blocks within a turn before execution) is the right highest-leverage backstop, and it has a specific property worth calling out for the implementation: the dedup has to happen after the blocks are emitted (since the model can't observe its own degeneration) but before any fork is handed to the execution layer. At that boundary, a content-hash on (subagent-type, prompt) is sufficient to collapse the duplicates — the model's description degradation across repeats (full → truncated) is just an artifact of the degeneration, not a signal of intended differentiation.

One thing that makes this harder at the harness side: unique tool_use IDs mean the harness's existing dedup logic (which likely keys on ID) won't catch this. The dedup has to key on content, not identity. If that seems expensive at scale: a count-per-subagent-type within a single turn is cheap and sufficient — if you see the same subagent type dispatched more than K times in one turn before any result returns, flag for dedup.

On the extended-thinking correlation (yurukusa's question upthread): the description-text degradation pattern (full → truncated) is characteristic of autoregressive output drift under extended generation, which extended thinking can amplify by holding more context in-flight without yielding. If this batch was running with thinking enabled, that's load-bearing for attributing this to the Cluster 13 regression window (v2.1.156–v2.1.158). If not, it's a structurally independent cluster.

We've hit this shape in an orchestration context (Claudiverse — claudeverse.ai) where the coordinator layer sits outside the CC harness and can see the fan-out count before committing. A content-hash dedup at the dispatch boundary is exactly what we ended up building — the principle is the same whether the intercepting layer is inside the harness (Fix #1) or external to it. External works as a workaround; the harness fix is the right permanent answer because it prevents the execution cost regardless of what tooling sits on top.

yurukusa · 3 months ago

@kcarriedo — The "18 dispatches with zero interleaved results" forensic is the kind of harness-side observable that closes the explanatory loop without requiring model-internal access. Two implications I want to add to your framing:

On the within-turn vs between-turn distinction. The property is visible in the emitted block stream before any execution completes, which makes it asymmetrically cheap to detect compared to all the after-the-fact approaches we've been working with (post-execution tool_use_id correlation, transcript replay, etc.). Detection cost is O(block-stream scan); detection happens at the right layer (harness, pre-dispatch); the corresponding intervention (Fix #1: dedup identical parallel tool_use blocks within a turn before execution) lives at the same layer. That's a closed-loop fix shape — observation, detection, and intervention all in the same component, which is the cheapest fix surface available across the cluster.

On the duplicate vs the multi-instance distinction. The 18-with-0-interleaved forensic also gives us a clean way to split sub-axis 22B (parallel-batch fabrication compounding) from a hypothetical 22B′ (legitimate multi-instance dispatch — e.g., parallel grep across different paths where each call has different inputs). The fabrication shape has interleaved-result=0 because nothing is actually executing under the hood; the legitimate multi-instance shape has interleaved-result>0 because results arrive incrementally as workers complete. A harness-side detector that surfaces "interleaved-result=0 over N>5 dispatches within a single autoregressive pass" as a stop-and-prompt event would catch the fabrication shape without false-positiving the legitimate parallel-grep case. Worth pinning as a separate detection signal from the tool_use_id pairing approach already shipped in PR #519; the two are complementary rather than overlapping.

I think your forensic is also the cleanest way to discriminate Cluster 22 from Cluster 25 (post-execution tool-result delivery failure + compensation cost). Cluster 25 has interleaved-result>0 (results do arrive, but late or partially); Cluster 22 with the 18-zero-interleaved signature is structurally separable. Adding this to the Cluster 22 candidate field guide revision; the "no interleaved result over N>5" detection signal is much sharper than what the guide has today.

@palios-taey — your supervisor-coordinated framing has held up across two more days of independent reports. The worker-retry-with-different-params leak shape (Cluster 25 candidate sibling) is now an entry on the cluster-tracker, and your articulation that the cost lives at the supervisor's coordination boundary — not at any individual vendor — is the single sentence I keep coming back to when distinguishing this cluster from single-vendor cost guides.

Three artifacts shipped today (2026-06-01) for the multi-vendor fleet path; flagging in case useful to your reading list or to anyone else who lands here from a similar fleet shape:

  • English long-edition field guide (gist 82055eb, ~2,754 words, MIT) — articulates the three cost shapes (deduplication, claim-and-judging, reasoning-cost-vs-execution-cost split) for fleets running Claude Code + Codex + Gemini + Grok in parallel. The framing leans on the supervisor-coordination boundary you drew in this thread; corrections welcome.
  • 5-question fit diagnostic (gist dcf6d896, browser-only, no signup, no telemetry) — for operators deciding whether they're in the multi-vendor cluster vs the single-vendor cluster. Five questions, scored locally.
  • Japanese preview (gist 6b9ddcd1, ~1,800 chars) — same three-cost-shape framing for Japanese-speaking operators.

A Japanese long-form paid handbook is shipping into the same pipeline (~18,000 chars at launch, ~11,000 free preview / ~6,500 paid, deeper sections forthcoming on per-vendor cost profiles, orchestrator construction templates, and failure-mode catalogue); will follow up with the live URL once the deployment chain completes. No CTA shape here; it's a parallel articulation in a different distribution layer, and the structural claim is the same as the field guide.

The 14-day measurement window I'm tracking ends 2026-06-14: new multi-vendor fleet filings, gist views, independent reports of the worker-retry coordination leak shape outside this thread. If the cluster has surface area beyond your fleet, the window should surface independent confirmations; if it doesn't, the cluster is single-operator scope and the articulation retires into the single-operator-handbook category rather than the cluster-handbook category. Either result is useful structural information for the next cycle.

kcarriedo · 3 months ago

The 4× fan-out you're hitting is one of the nastier failure modes in multi-agent orchestration — the model degenerates on a single non-yielding turn, the harness has no dedup layer, and suddenly you've got 24 agents burning tokens that were supposed to be 6.

The harness-side dedup you proposed (collapse exact-duplicate tool_use blocks within a single turn) is the right first backstop. The subtlety worth noting: even near-duplicate prompts (not exact-match) can produce semantically redundant work — the agent descriptions collapsing from full text to truncated form across iterations is the autoregressive decay fingerprint, not just random variation.

One pattern that helps at the orchestration level before harness fixes land: an external process that tracks which agent dispatches have already been submitted (by signature hash of type + prompt) and refuses to pass duplicates through to the executor. This works as an out-of-process guard but requires that the orchestration layer be externalizable — which is exactly what's missing in single-process Claude Code today.

Claudeverse (claudeverse.ai) is building this coordination layer — session lifecycle tracking, duplicate dispatch detection, and fan-out caps as first-class primitives — if you want to follow along or kick the tires while the harness fix works through the queue.

nicolafavero · 2 months ago

Confirming this from a mitigation-experiment angle, with one empirical datapoint
that I think narrows the solution space.

TL;DR: I built PreToolUse hooks specifically to catch redundant/duplicate
tool calls, and they structurally cannot catch the within-turn duplication this
issue describes — which corroborates the OP's "fix must be at the execution
layer" point with evidence rather than argument.

What I tried. After observing repeated bursts of duplicate/equivalent
parallel calls in a single message (literal dupes like git X twice, plus
unfiltered find/ls dumps), I added PreToolUse hooks to block/warn on them.
Two regex-based rules: one blocking duplicated git sub-commands *within a single
command string*, one warning on unfiltered find/ls.

Why it doesn't work for the case in this issue. PreToolUse fires per tool
call and is blind to its sibling tool_use blocks in the same assistant
message
. It can catch an intra-command duplicate (git diff … git diff inside
one Bash string), but it has no visibility into the cross-call case — N identical
tool_use blocks emitted together, or the same batch re-emitted within one
non-yielding turn (your 6→24). The hook sees each block in isolation, with no
shared state across the batch, so the dominant failure mode passes straight
through. This matches the OP exactly: the reliable fix is at the layer that turns
emitted blocks into executed work, not at a per-call gate (and not at a prompt
rule — I had a passive "don't batch duplicates" instruction loaded *and cited in
the same turn* it failed).

What partially works. The only layer with batch-level visibility that a user
can reach today is a post-hoc one — a Stop-event hook reading the turn
transcript to detect identical/equivalent tool-call signatures emitted in the
same turn and surface a blocking reminder. It doesn't prevent the first wasted
emission, but it closes the feedback loop actively instead of passively.

On the harness fixes proposed here. The within-turn dedup of identical
parallel tool_use blocks (your fix #1) and a soft fan-out cap (#2) are the only
things that would have caught my cases — both sit at exactly the layer a
per-call hook can't reach.

Release note for triagers: v2.1.161 addressed the adjacent *cancellation-
cascade* axis (a failed Bash call no longer cancels its sibling parallel calls),
but that's orthogonal to this issue — it changes failure propagation within a
batch, not the re-emission/duplication of the batch itself. This one is still
open and uncovered.

(Environment note: observed on Opus-class models with extended thinking. I can't
make a cross-model causal claim from inside a session, so I'm not asserting one —
just adding it as a consistent data point alongside the other reports here.)

lkyioeu · 2 months ago

Additional case: non-parallel sequential tool calls also duplicated

I'm hitting the same class of bug, but with sequential (non-parallel) tool calls — not just subagent fan-out.

What happened

During a session managing skills (), the following tool calls were silently duplicated:

  1. Bash — executed 45 times instead of 1 (same command, same args, all returned success)
  2. Edit (settings.json) — executed 4 times instead of 1, writing the same field 3 times into the file
  3. Write (memory .md file) — executed 4 times instead of 1

These were all sequential tool calls within a single assistant turn, not parallel subagent dispatches.

Environment

  • Claude Code CLI (latest, using third-party API proxy)
  • Windows 11
  • Model: mimo-v2.5-pro (via proxy, not direct Anthropic API)
  • Session had extensive skill listings and file operations

Key observations

  • The first execution always succeeds — subsequent duplicates are wasted no-ops
  • From the model's perspective, each call returned the same result, creating a loop
  • The user had to interrupt manually to stop the repetition
  • No dedup or warning was triggered

Relation to this issue

This confirms the pattern described here: the harness executes every emitted block without deduplication. The difference is that in my case, the repetition happens across sequential tool calls (model re-emits the same call after receiving the result), not just within a single parallel batch.

This may also be related to #41750 (context management triggering re-emission).

Suggested fix alignment

Strong +1 on the suggested fixes above. Additionally:

  1. Deduplicate by tool signature + args within a turn — if the same tool is called with identical args more than N times (e.g., 2), block subsequent calls and warn the user
  2. Surface a repetition warning in the UI — even without blocking, show "⚠️ Detected repeated tool call: rm -rf ... (executed 5 times)" so users can interrupt earlier
github-actions[bot] · 1 month ago

Closing for now — inactive for too long. Please open a new issue if this is still relevant.