claude-code - 💡(How to fix) Fix [BUG] Agent Teams: lead session loops on idle notifications and duplicate task_assignment echoes, burns ~13–22% of input tokens on no-op acks [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#47930Fetched 2026-04-15 06:38:18
View on GitHub
Comments
1
Participants
2
Timeline
8
Reactions
2
Timeline (top)
labeled ×5subscribed ×2commented ×1

Error Message

Error Messages/Logs

No error. The waste is silent. Representative trace of what the lead sees, back to back, each as its own lead turn (sanitized):

Fix Action

Fix / Workaround

  1. Idle-notification turns. After every teammate turn ends, the lead receives a {"type":"idle_notification","from":"dev-N","idleReason":"available"} message as a fresh turn. For 8 teammates completing 2–3 turns each, this produces 20–40 lead-turn firings whose entire work is "acknowledge idle."
  2. Duplicate task_assignment echoes. Teammates report receiving stale/self-originating dispatches for tasks they've already completed. Each echo triggers a teammate turn → a new completion message → another lead turn → another idle notification loop. One teammate wrote: "Received a task_assignment for #3 that appears to be from my own agent ID (likely an echo/stale queue message) — ignoring."
  3. Lead never gracefully stops reacting. Even after every task is marked completed, idle notifications keep arriving for several minutes. The lead keeps answering each one.

Token multiplier vs equivalent non-team dispatch: comparable work done in earlier sessions via plain Agent(run_in_background: true) × 5 (no TeamCreate, no shared task list) completes in ~30–40 lead turns with no idle/echo cost. Teams-mode paid ~138 turns for the same delivered artifacts — a ~3–4× lead-turn inflation, roughly half of which is the loop bug.

  1. Idle-notification deliveries should not generate a new lead turn by default. Treat them like a presence update — surface in UI, but do not wake the lead model.
  2. task_assignment dispatches should be suppressed when the target task status is already completed, ideally at the team service layer before the teammate mailbox.
  3. Self-originated deliveries (sender agentId == recipient agentId) should be dropped on send.
  4. Provide an env toggle like CLAUDE_CODE_TEAM_LEAD_SUPPRESS_IDLE=1 for users who want current behaviour off until a better default ships.

Code Example

<teammate-message teammate_id="dev-3" color="yellow">
{"type":"idle_notification","from":"dev-3","timestamp":"2026-04-14T12:37:38.194Z","idleReason":"available"}
</teammate-message>

<teammate-message teammate_id="dev-4" color="purple" summary="Task #4 already completed — no-op">
Received what looks like a re-echo of the original task #4 assignment
(sender field is my own ID). Task #4 is already marked completed — no
additional work performed. Standing by.
</teammate-message>

<teammate-message teammate_id="dev-4" color="purple">
{"type":"idle_notification","from":"dev-4","timestamp":"2026-04-14T12:37:58.164Z","idleReason":"available"}
</teammate-message>

<teammate-message teammate_id="dev-3" color="yellow" summary="Task #3 already done — no-op on re-assignment">
Received a task_assignment echo for #3, but that task is already
completed. Status confirmed via TaskGet: #3 = completed. No further
action — standing by.
</teammate-message>

<teammate-message teammate_id="dev-3" color="yellow">
{"type":"idle_notification","from":"dev-3","timestamp":"2026-04-14T12:38:00.473Z","idleReason":"available"}
</teammate-message>

---

TeamCreate(team_name: "repro")
   TaskCreate x 5   # distinct tasks, no dependencies
   Agent(subagent_type: "fullstack-developer", model: "opus", run_in_background: true,
         team_name: "repro", name: "dev-N")   # x 5

---

python3 - <<'PY'
import json, glob, os
newest = max(glob.glob(os.path.expanduser("~/.claude/projects/*/*.jsonl")), key=os.path.getmtime)
idle=echo=total=0
tokens_idle=tokens_total=0
with open(newest) as f:
    entries = [json.loads(l) for l in f if l.strip()]
for i,r in enumerate(entries):
    if r.get("type") != "assistant": continue
    u = (r.get("message") or {}).get("usage") or {}
    if not u: continue
    cost = u.get("input_tokens",0)+u.get("cache_creation_input_tokens",0)+u.get("cache_read_input_tokens",0)
    total += 1; tokens_total += cost
    prev = next((entries[j] for j in range(i-1,-1,-1) if entries[j].get("type")=="user"), {})
    p = json.dumps(prev)
    if '"idle_notification"' in p and '<teammate-message' in p and len(p) < 3000:
        idle += 1; tokens_idle += cost
    elif "already completed" in p or "duplicate" in p.lower():
        echo += 1; tokens_idle += cost
print(f"{newest}")
print(f"idle+echo turns: {idle+echo}/{total} ({100*(idle+echo)/max(1,total):.1f}%)")
print(f"idle+echo input tokens: {tokens_idle:,} / {tokens_total:,} ({100*tokens_idle/max(1,tokens_total):.1f}%)")
PY
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • I am using the latest version of Claude Code

What's Wrong?

When orchestrating a team of 5–8 teammates via Agent Teams (CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1), the lead session gets wedged into an acknowledgement loop that costs a full lead turn for every notification:

  1. Idle-notification turns. After every teammate turn ends, the lead receives a {"type":"idle_notification","from":"dev-N","idleReason":"available"} message as a fresh turn. For 8 teammates completing 2–3 turns each, this produces 20–40 lead-turn firings whose entire work is "acknowledge idle."
  2. Duplicate task_assignment echoes. Teammates report receiving stale/self-originating dispatches for tasks they've already completed. Each echo triggers a teammate turn → a new completion message → another lead turn → another idle notification loop. One teammate wrote: "Received a task_assignment for #3 that appears to be from my own agent ID (likely an echo/stale queue message) — ignoring."
  3. Lead never gracefully stops reacting. Even after every task is marked completed, idle notifications keep arriving for several minutes. The lead keeps answering each one.

Net effect: a piece of coordination work that should take ~6–8 lead turns takes 60–100. Token consumption scales with the product (teammates × notifications), not with actual work.

Token-cost evidence from a real session

I instrumented the transcript JSONL of a single session that orchestrated 8 teammates (1× TeamCreate + 5 TaskCreate initial + 3 incremental tasks, all tasks completed, no rework). Classified every lead turn by what message triggered it:

TriggerTurnsInput tokens% of teams-portion
Pure idle-notification (lead "ack")132,218,1789.5 %
Pure duplicate/echo ack5806,1143.5 %
Human-typed instruction122,280,6309.8 %
Tool-result follow-up (useful work)9415,824,99368.0 %
Lead woke by teammate msg, no human input142,153,7919.3 %
TOTAL (teams portion)13823,283,706100 %

Strict noise from the loop bug (idle-only + duplicate-ack turns where the human did not instruct anything):

18 turns / 3.03 M input tokens = 13.0 % of the Teams-portion input spend, paid purely to say "acknowledged" to notifications the skill docs say I should ignore.

Broader noise (every turn where the lead was woken by a teammate message with no human instruction in the same turn):

32 turns / 5.18 M input tokens = 22.2 % of the Teams-portion input spend.

For comparison, the same session before entering Teams mode (the ~682 pre-team turns doing regular Agent tool calls, reads, edits, etc.) averaged similar per-turn input (~157 K vs ~167 K), but had 0 idle-ack turns and 0 duplicate-completion acks. The loop behaviour is unique to Teams mode.

Token multiplier vs equivalent non-team dispatch: comparable work done in earlier sessions via plain Agent(run_in_background: true) × 5 (no TeamCreate, no shared task list) completes in ~30–40 lead turns with no idle/echo cost. Teams-mode paid ~138 turns for the same delivered artifacts — a ~3–4× lead-turn inflation, roughly half of which is the loop bug.

What Should Happen?

One or more of:

  1. Idle-notification deliveries should not generate a new lead turn by default. Treat them like a presence update — surface in UI, but do not wake the lead model.
  2. task_assignment dispatches should be suppressed when the target task status is already completed, ideally at the team service layer before the teammate mailbox.
  3. Self-originated deliveries (sender agentId == recipient agentId) should be dropped on send.
  4. Provide an env toggle like CLAUDE_CODE_TEAM_LEAD_SUPPRESS_IDLE=1 for users who want current behaviour off until a better default ships.

Error Messages/Logs

No error. The waste is silent. Representative trace of what the lead sees, back to back, each as its own lead turn (sanitized):

<teammate-message teammate_id="dev-3" color="yellow">
{"type":"idle_notification","from":"dev-3","timestamp":"2026-04-14T12:37:38.194Z","idleReason":"available"}
</teammate-message>

<teammate-message teammate_id="dev-4" color="purple" summary="Task #4 already completed — no-op">
Received what looks like a re-echo of the original task #4 assignment
(sender field is my own ID). Task #4 is already marked completed — no
additional work performed. Standing by.
</teammate-message>

<teammate-message teammate_id="dev-4" color="purple">
{"type":"idle_notification","from":"dev-4","timestamp":"2026-04-14T12:37:58.164Z","idleReason":"available"}
</teammate-message>

<teammate-message teammate_id="dev-3" color="yellow" summary="Task #3 already done — no-op on re-assignment">
Received a task_assignment echo for #3, but that task is already
completed. Status confirmed via TaskGet: #3 = completed. No further
action — standing by.
</teammate-message>

<teammate-message teammate_id="dev-3" color="yellow">
{"type":"idle_notification","from":"dev-3","timestamp":"2026-04-14T12:38:00.473Z","idleReason":"available"}
</teammate-message>

Each of the five blocks above arrived as a separate lead-model turn (~167 K input tokens each at cache-read rates). The lead's reply to each is effectively "acknowledged" — and then another idle notification arrives.

Steps to Reproduce

  1. Enable Agent Teams: "env": { "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1" } in ~/.claude/settings.json.
  2. From a lead session, run:
    TeamCreate(team_name: "repro")
    TaskCreate x 5   # distinct tasks, no dependencies
    Agent(subagent_type: "fullstack-developer", model: "opus", run_in_background: true,
          team_name: "repro", name: "dev-N")   # x 5
  3. Let the 5 teammates complete their tasks.
  4. Watch the lead transcript: for every teammate that marks a task completed, expect 2–4 idle_notification turns and 1–2 task_assignment echo responses, each as its own lead turn.
  5. Grep your session JSONL for "idle_notification" and "already completed" to quantify noise-vs-signal.

Pressing Esc on the lead breaks the loop (the queued notification turns are discarded) and the lead resumes normal behaviour when told to continue.

Quantifying in your own session

Drop this into a shell (adjust the project dir):

python3 - <<'PY'
import json, glob, os
newest = max(glob.glob(os.path.expanduser("~/.claude/projects/*/*.jsonl")), key=os.path.getmtime)
idle=echo=total=0
tokens_idle=tokens_total=0
with open(newest) as f:
    entries = [json.loads(l) for l in f if l.strip()]
for i,r in enumerate(entries):
    if r.get("type") != "assistant": continue
    u = (r.get("message") or {}).get("usage") or {}
    if not u: continue
    cost = u.get("input_tokens",0)+u.get("cache_creation_input_tokens",0)+u.get("cache_read_input_tokens",0)
    total += 1; tokens_total += cost
    prev = next((entries[j] for j in range(i-1,-1,-1) if entries[j].get("type")=="user"), {})
    p = json.dumps(prev)
    if '"idle_notification"' in p and '<teammate-message' in p and len(p) < 3000:
        idle += 1; tokens_idle += cost
    elif "already completed" in p or "duplicate" in p.lower():
        echo += 1; tokens_idle += cost
print(f"{newest}")
print(f"idle+echo turns: {idle+echo}/{total} ({100*(idle+echo)/max(1,total):.1f}%)")
print(f"idle+echo input tokens: {tokens_idle:,} / {tokens_total:,} ({100*tokens_idle/max(1,tokens_total):.1f}%)")
PY

Claude Model

Opus (claude-opus-4-6)

Is this a regression?

Not sure — this is my first extended use of Agent Teams so I can't point to a "last working" version. Filing under "likely always-been-this-way" rather than regression.

Last Working Version

No response

Claude Code Version

Claude Code 2.1.107

Platform

Claude Pro/Max subscription

Operating System

macOS (darwin 25.3.0)

Terminal/Shell

Terminal.app / zsh

Additional Information

  • Esc + "continue" reliably breaks the loop, but requires human babysitting, which defeats the purpose of background teams.
  • Teammates themselves correctly detect the stale traffic (they explicitly write "appears to be from my own agent ID" in replies) but still must emit a full response turn per delivery, which then wakes the lead. A fix at either end (suppress delivery or suppress model-turn trigger) would resolve the loop.
  • The 13–22 % noise share quoted above is on a single 8-teammate session that ran to completion without rework; longer sessions with more inter-teammate DMs would be higher.
  • Happy to share a trimmed, anonymized JSONL slice if the team needs a deterministic reproducer.

extent analysis

TL;DR

The most likely fix for the issue is to suppress idle notifications from generating a new lead turn by default, and to drop self-originated deliveries.

Guidance

  • Investigate the CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS feature and its interaction with idle notifications and task assignments to understand the root cause of the loop.
  • Consider implementing a filter to drop self-originated deliveries (sender agentId == recipient agentId) to prevent echo responses.
  • Review the team service layer to determine if task_assignment dispatches can be suppressed when the target task status is already completed.
  • Evaluate the feasibility of introducing an environment toggle, such as CLAUDE_CODE_TEAM_LEAD_SUPPRESS_IDLE, to allow users to opt-out of the current behavior.

Example

No code example is provided as the issue is more related to the logic and behavior of the Agent Teams feature rather than a specific code snippet.

Notes

The issue seems to be specific to the Agent Teams feature and the interaction between idle notifications, task assignments, and self-originated deliveries. The provided Python script can be used to quantify the noise caused by the loop in a given session.

Recommendation

Apply a workaround by implementing a filter to drop self-originated deliveries and suppressing idle notifications from generating a new lead turn by default, until a more permanent fix is available. This should help reduce the noise caused by the loop and improve the overall efficiency of the Agent Teams feature.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix [BUG] Agent Teams: lead session loops on idle notifications and duplicate task_assignment echoes, burns ~13–22% of input tokens on no-op acks [1 comments, 2 participants]