[BUG] Agent Teams: lead session loops on idle notifications and duplicate task_assignment echoes, burns ~13–22% of input tokens on no-op acks
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] 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:
- 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." - Duplicate
task_assignmentechoes. 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." - 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:
| Trigger | Turns | Input tokens | % of teams-portion |
|---|---:|---:|---:|
| Pure idle-notification (lead "ack") | 13 | 2,218,178 | 9.5 % |
| Pure duplicate/echo ack | 5 | 806,114 | 3.5 % |
| Human-typed instruction | 12 | 2,280,630 | 9.8 % |
| Tool-result follow-up (useful work) | 94 | 15,824,993 | 68.0 % |
| Lead woke by teammate msg, no human input | 14 | 2,153,791 | 9.3 % |
| TOTAL (teams portion) | 138 | 23,283,706 | 100 % |
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:
- 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.
task_assignmentdispatches should be suppressed when the target task status is alreadycompleted, ideally at the team service layer before the teammate mailbox.- Self-originated deliveries (sender
agentId== recipientagentId) should be dropped on send. - Provide an env toggle like
CLAUDE_CODE_TEAM_LEAD_SUPPRESS_IDLE=1for 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
- Enable Agent Teams:
"env": { "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1" }in~/.claude/settings.json. - 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
- Let the 5 teammates complete their tasks.
- Watch the lead transcript: for every teammate that marks a task completed, expect 2–4
idle_notificationturns and 1–2task_assignmentecho responses, each as its own lead turn. - 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.
This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗