Skip to content

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

Open

Description

@umitkacar

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")

  2. 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.

Activity

  1. ForkyTheBot commented on Feb 20, 2026

    @ForkyTheBot

    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.

  2. Ipiano commented on Jun 16, 2026

    @Ipiano

    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.
  3. postoso commented on Jul 16, 2026

    @postoso

    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.

  4. zhanghuafei commented on Jul 17, 2026

    @zhanghuafei

    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.

  5. Burlesque1 commented on Aug 21, 2026

    @Burlesque1

    Additional evidence (v2.1.233, background Workflow + StructuredOutput schema):

    Same defect surface as #20754 (background-task completion notification not delivered), but here there is no multi-agent concurrency race — a single background Workflow with two subagents that finished sequentially (~5 min apart), and the main loop still never received a completion notification.

    What happened

    • Both subagents (Architect + Critic) completed normally, each submitting findings via StructuredOutput (~4KB JSON each); transcripts end with "Structured output provided successfully".
    • The workflow's journal.jsonl has both result events written, but no completed event, and went silent for 14+ minutes after the last write.
    • The main-thread transcript stopped writing right after the workflow was launched (TaskCreate/Update) and has stayed parked waiting for <task-notification>; the task spinner in the UI accumulates wall-clock indefinitely.
    • No error artifacts anywhere.

    Evidence

    • wf run: wf_555bb1aa-709 (session 733ee9da-4fa6-4bd3-9651-9c22bb78e358)
    • subagents/workflows/wf_555bb1aa-709/journal.jsonl: two result events, no completed
    • Both agent-*.jsonl transcripts end with a successful StructuredOutput

    How this differs from / adds to #20754

    • Background Task Notifications Not Delivered When Multiple Agents Complete Simultaneously (v2.1.19) #20754 points at a notification-queue race (multiple agents completing simultaneously + a user message sent mid-run).
    • This case is a single Workflow, agents completing at different times, no user message mid-run — yet delivery was still lost. That suggests the failure is not only queue concurrency: the delivery path can silently drop the "done -> notify main loop" step on its own.
    • A large StructuredOutput result (critic ~4KB JSON in one call) may be an aggravating factor.

    Suggested fix

    Add a watchdog on the completion-delivery step for background workflows / background tasks: if the computation finishes but the notification is not delivered within a timeout, surface an explicit error and point to journal.jsonl/the transcripts — instead of letting the main-loop spinner spin forever. The journal is effectively the source of truth; when the notification is lost, state should also be derivable from it and the main loop resumed from there.

  6. postoso commented on Aug 25, 2026

    @postoso

    2.1.243's background-bash wake fix verified on our side — with a same-night A/B against 2.1.241, and two boundaries worth noting.

    Setup: unnamed background subagents (Task tool) each fire one run_in_background Bash (sleep 45–390s) and end their turn parked. We then adjudicate each agent's own transcript (subagents/agent-*.jsonl) rather than trusting self-reports: a wake = an isMeta background-task event whose tool_use_id matches the agent's own backgrounded Bash call, followed by a post-wake assistant turn.

    • 2.1.243: 14/14 owner-wakes, across simultaneous completions (two landing 1 s apart), serialized completions, depth-2 (manager under manager), >5-minute parks (390 s at depth 1 AND depth 2), a loaded system (8 concurrent agents), and two overlapping bashes per agent — each completion produced its own wake, so "their last background Bash task" in the changelog is not a limiter.
    • 2.1.241 control, same night, same machine (archived binary from ~/.local/share/claude/versions/ + DISABLE_AUTOUPDATER=1): 0/3 — the parked agents never woke, and the completion notifications reached neither the agents nor the root session (queued, never delivered; a later manual resume flushed them).

    Two boundaries observed on 2.1.243:

    1. Named teammates (in_process_teammate) still get no wake — 0/2, still parked 10+ minutes after their bash exited 0. If the fix was meant to cover them, that half isn't working; if "background subagents" is literal, this is just the scope line.
    2. Resumed-child completion routing is unchanged (possibly out of scope for this issue): after a parent SendMessage-resumes a completed child, that child's next completion routed to the ROOT session instead of the resuming parent in 5 of 6 instrumented trials (nondeterministic).

    Happy to share transcript excerpts or the drill procedure if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:corebugSomething isn't workinghas reproHas detailed reproduction stepsplatform:macosIssue specifically occurs on macOSstaleIssue is inactive

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions