Background Task Notifications Not Delivered When Multiple Agents Complete Simultaneously (v2.1.19)

Status Open
Reported on v2.1.19
Maintainer reply None cached
Activity 6 comments · opened Jan 25, 2026

Bug Description

When running multiple background agents (run_in_background: true) in parallel, completion notifications are not consistently delivered to the main session. Only 1 out of 3 agents sent a notification upon completion.

Environment

  • Claude Code Version: 2.1.19
  • OS: macOS Sequoia 15.2 (Darwin 25.2.0)
  • Model: claude-opus-4-5-20251101
  • Terminal: iTerm2

Steps to Reproduce

  1. Launch 3 background agents simultaneously:
Task({
  subagent_type: "general-purpose",
  description: "Task 1",
  prompt: "...",
  run_in_background: true
})
// Repeat for Task 2 and Task 3
  1. While agents are running, send a user message (e.g., "checking status")
  1. Wait for all agents to complete

Expected Behavior

All 3 agents should deliver <task-notification> messages upon completion.

Actual Behavior

  • Agent 1 (a34bb07): ✅ Notification received
  • Agent 2 (a0fc025): ❌ No notification (but completed successfully)
  • Agent 3 (a194492): ❌ No notification (but completed successfully)

All agents completed successfully (verified via tail on output files), but only 1 notification was delivered to the main session.

Evidence

Output files confirm completion:

$ grep -c "stop_reason" ~/.claude/projects/.../subagents/agent-*.jsonl
agent-a0fc025.jsonl:22  # Completed
agent-a194492.jsonl:41  # Completed  
agent-a34bb07.jsonl:42  # Completed (notification received)

Suspected Cause

Race condition in notification queue processing:

  1. Multiple agents complete around the same time
  2. User sends a message while notifications are queued
  3. Queue processing is interrupted or notifications are "swallowed"

Workaround

Manually check agent output files:

tail -20 /private/tmp/claude/-Users-*/tasks/*.output

Or wait a few seconds after agents complete before sending any messages.

Impact

  • Users cannot reliably track background task completion
  • Workflow automation based on notifications becomes unreliable
  • Parallel agent execution loses observability

Related

This may be related to the notification queue handling introduced in v2.1.16+ with the new task management system.

---

Thank you for looking into this! Happy to provide additional logs or context.

View original on GitHub ↗

4 Comments

ForkyTheBot · 6 months ago

This notification delivery bug is brutal for multi-agent workflows. If you can't rely on completion notifications, you're back to polling - which defeats the whole point of background agents.

The Root Problem

Claude's internal notification system is:

  • Best-effort delivery (not guaranteed)
  • Same-process bound (notifications via terminal output)
  • No external notification hooks
  • No mobile notifications

When multiple agents complete simultaneously, the notification queue likely gets overwhelmed or race-conditions occur.

External Notification Layer

We built ForkOff specifically to solve unreliable internal notifications:

How It Works

Instead of relying on Claude's stdout for notifications:

  1. Agent completion → fires webhook to external relay
  2. Relay → guaranteed push notification to mobile
  3. Phone buzzes → "Agent X completed"
  4. Works every time (not best-effort)

Architecture Benefits

Decoupled from Claude:

  • Notifications don't depend on Claude's internal event system
  • Works even if Claude crashes/hangs
  • Can notify multiple devices
  • Audit trail of all completions

Webhook-based:

# In agent completion script
curl -X POST https://relay.forkoff.app/notify \
  -d "agent_id=$AGENT_ID" \
  -d "status=complete" \
  -d "result=$RESULT"

Guaranteed delivery:

  • Relay persists notification until acknowledged
  • Retries on failure
  • Mobile app shows missed notifications on open

Use Case: Your 3-Agent Scenario

With current system:

  • Agent 1, 2, 3 complete simultaneously
  • Only 1 notification shows up
  • You don't know other 2 finished
  • Have to manually check

With external notifications:

  • All 3 fire webhooks
  • All 3 push to phone
  • Phone shows 3 completion badges
  • Tap each to see results

Beta Access

Just launched: https://forkoff.app (TestFlight)

Workaround Until Claude Fixes This

Add webhook calls to your agent completion logic as a safety net. Even when Claude's notifications work, you get redundant external notification.

When they fail (like this bug), external system is backup.

---
Disclosure: I work on ForkOff. Built this after hitting the same "where's my notification?" frustration.

Ipiano · 2 months ago

More evidence.. Same root symptom, confirmed across 2.1.170 through 2.1.178.

What's Wrong?

A completed background/async task intermittently fails to resume the agent waiting on it. Two observed manifestations, same user-visible result:

  • (a) Misroute: the completion is injected as a turn into a different, already-finished sibling agent instead of the launcher. The launcher never hears about it; the sibling is spuriously revived and emits a mislabeled notification.
  • (b) No-resume: the completion is delivered into the correct agent's transcript but never triggers the next turn, so the agent stalls on a finished result.

Either way the waiting agent hangs indefinitely until killed or manually nudged. It is load-dependent: the more tasks complete near-simultaneously, the higher the failure rate.

What Should Happen?

Every completed task resumes the agent that launched it, exactly once, in that agent's own context.

Error Logs/Messages

Manifestation (b): minimal repro, two sibling agents on v2.1.170, identical sleep 15 && echo task, diverging only at resume:

| | healthy agent | stuck agent |
|---|---|---|
| Bash issued | 16:07:49 | 16:07:51 |
| TOOL_RESULT delivered | 16:08:04 MARKER_1 (is_error:false) | 16:08:06 MARKER_4 (is_error:false) |
| resumed to next turn | 16:08:06 "MARKER_1 done" (ok) | never; stalled 6 min until process timeout |

The stuck agent's successful tool result is present in its transcript; the loop was simply never re-invoked. No rate-limit/429/quota/error anywhere in its transcript or stderr.

Manifestation (a): main-session queue log (v2.1.177), completion removed without delivery, then surfacing inside a sibling:

enqueue -> MAIN   notif{ task=A, tuid=A }     # correct ids/content
remove  <- MAIN                               # pulled out, NOT dequeued/delivered
                  -> injected as a user turn inside already-finished sibling B
enqueue -> MAIN   notif{ task=B, "B's name" } # B's identity, A's content (mislabeled)

Steps to Reproduce

  1. From a session, launch 5 or more background agents (run_in_background) at once, each doing a short identical task (e.g. sleep 15 && echo MARKER_<n>), then instruct the launcher to wait for all completions.
  2. Observe that one or more agents finish but never resume / never report back, and the launcher stalls. In headless -p runs, the stalled child is eventually killed by timeout (exit 124).
  3. This is a race with a low per-task rate. A single 5-agent trial frequently does not trigger it (a known-affected version ran clean in one such trial). Reliable detection needs many concurrent tasks and/or repeated trials.

Objective check (do not trust the model's narration): in the session JSONL, grep queue-operation entries with "operation":"remove" on a <task-notification>; and scan subagents/agent-*.jsonl for a <task-notification> whose task-id does not match that file's agent id but is an agent the main session launched. Either is a confirmed instance.

Last Working Version

2.1.152, the most recent version I have observed working with background agents (17 launches across 3 sessions, 0 failures). The bug is present in 2.1.170, 2.1.177, and 2.1.178. I have no background-agent usage between 2.1.153 and 2.1.176, so the exact regression point is unknown.

Additional Information

  • Not caused by nested subagents (2.1.172). Reproduced cleanly on 2.1.170, which predates that feature, so this should not be treated as a nesting regression.
  • Scope in real usage: 31 confirmed sibling-misroutes (manifestation a) across heavily-concurrent sessions on 2.1.177; 0 in sessions that ran agents serially. The no-resume variant (b) reproduced live on 2.1.170 and 2.1.178.
  • Strongly correlated with concurrency: a just-completed sibling still resident when the next task finishes.
postoso · 1 month ago

Regression data from v2.1.211 (controlled drills, interactive harness, macOS): background-Bash park-wake delivery has degraded from "unreliable" to effectively zero.

Setup: subagents pinned model="sonnet" under an Opus main, each running a short sleep via Bash run_in_background=true and ending their turn parked. Verified from the agent JSONL transcripts (presence/absence of the isMeta background-task wake event), not from agent self-reports.

  • Simultaneous completions (3 agents, identical sleeps, launched together — the known-worst case): 0/3 woke. All three transcripts end at the parked turn with zero wake events; each sat parked until manually bumped via SendMessage.
  • Serialized completions (4 agents run strictly one-at-a-time in a quiet session with short sleeps — the favorable case): 0/4 woke. Same signature.
  • Historical baseline on earlier builds was ~1-in-3 delivered (field measurements, July 2026), so this looks like a further regression, not just the known simultaneity failure mode.
  • Control: the child-Agent completion channel works — parked agents woken by their own Task/Agent child completing resumed 4/4 in the same drills, including one at nesting depth 2.
  • Headless claude -p (same version): parked subagents never wake on either channel — neither a background-Bash completion nor a child-agent completion resumes them, whether the headless main is blocked in one long tool call or idling across many short ones. They stay frozen until the process exits.

In every case the completed notification did reach the root session, so the wake-routing (not the completion detection) appears to be where delivery dies. Happy to provide session/agent IDs or run variations if useful.

zhanghuafei · 1 month ago

Adding a reproducible variant: nested background task inside a background subagent never wakes it — result stranded until an external message arrives

We hit what appears to be an aggravated variant of this issue (and of the race described in #39632), in an interactive session on v2.1.191 (macOS).

Setup

  1. Main session spawns a subagent (teammate) via the Agent tool with run_in_background: true, to watch a k8s deployment rollout.
  2. The subagent starts a long-running polling script with Bash run_in_background: true, then ends its turn and goes idle (emits idle_notification).
  3. The script completes normally ~8 minutes later (exit 0, output file fully written to the session tasks dir at 11:25:44).

Observed

  • The task-completion notification never woke the idle subagent. No result processing, no message back to the main session.
  • The result sat stranded in the tasks dir for ~21 minutes, until the user noticed the deployment had long finished and the main session manually sent the subagent a message via SendMessage. Only then did it

wake up — at which point TaskStop reported the task no longer existed (already completed and cleaned up).

  • Control group, same session, same day: two background Bash tasks started directly by the main session (CI build waiters, 10–20 min each) both delivered their completion notifications promptly and reliably. So

notification delivery to the main session works; it's the idle background subagent that never gets woken.

Impact

Any orchestration pattern of the form "background teammate babysits a long-running script and reports back" silently fails: the work completes, but the result never returns. In our run, 3 out of 4 spawned
teammates failed to proactively report results (this one being the worst case). That makes background subagents unreliable as autonomous workers for anything long-running.

Showing cached comments. Read the full discussion on GitHub ↗