Skip to content

stream-json: background-task notification doesn't wake idle session (race in runAsyncAgentLifecycle) #39632

Description

@dholtmannrice

Symptom: When a background agent completes while the main session is idle (between turns, waiting for stdin/FIFO), the <task-notification> sits in the queue indefinitely until the next user message triggers a turn. Observed gap: 1m51s idle with notification enqueued but undelivered.

Root cause (traced in v2.1.86-dev.20260326):

In runAsyncAgentLifecycle:

completeAgentTask(agentResult, rootSetAppState);        // task.status → "completed" → hasRunningBg = FALSE
let handoffWarning = await classifyHandoffIfNeeded(...); // async (runs when mode === "auto")
let worktreeResult = await getWorktreeResult();          // async
enqueueAgentNotification({...});                         // NOW hasQueued = TRUE

The do-while in runHeadlessStreaming polls every 100ms:

do {
  await drainCommandQueue();
  let hasRunningBg = getRunningTasks(state).some(...);
  let hasQueued = hasCommandsInQueue();
  if (hasRunningBg || hasQueued) { ...; await sleep(100); }
} while (waitingForAgents);

During the two await gaps in runAsyncAgentLifecycle, a poll tick fires, sees hasRunningBg=false && hasQueued=false, exits the do-while, emits result, and run() returns idle. The later enqueuePendingNotification (priority later) fires queueChanged.emit(), but stream-json's subscribeToCommandQueue handler only aborts for priority="now" — it doesn't re-trigger run().

REPL doesn't hit this: useSyncExternalStore(subscribeToCommandQueue) + useEffect → processQueueIfReady means any queueChanged emission triggers a drain.

Suggested fix: Reorder runAsyncAgentLifecycle to call enqueueAgentNotification before completeAgentTask sets status="completed" — the 100ms poll never sees both false. Alternatively: add a run() trigger to runHeadlessStreaming's subscribeToCommandQueue callback for any enqueue (not just priority=now), guarded by if (!running).

Repro trace: session 0e97d6af-ff32-4b68-8375-792a79ecbcdc, transcript lines 27852–27927. Enqueue at 20:43:08, dequeue at 20:45:52 (after next user prompt's turn).

Activity

  1. jungjoongi commented on Apr 9, 2026

    @jungjoongi

    Additional data from scheduled task session (v2.1.87, 2026-04-09)

    Confirming this race condition in a production scheduled task pipeline (headless, no user present).

    Setup

    • Orchestrator spawns 4–6 background agents simultaneously via Agent tool (run_in_background: true)
    • Scheduled task mode (no REPL — exactly the runHeadlessStreaming path described above)

    Results: 3/13 notifications silently dropped (23%)

    Writer batch (4 agents):

    Agent Completed Notification
    writer-batch-1 04:32:12 ✅
    writer-batch-2 04:32:20 ✅
    writer-batch-3 04:32:31 ✅
    writer-batch-4 04:32:40 ❌ dropped

    Fact-checker batch (6 agents):

    Agent Completed Notification
    fc-batch-1 07:49:46 ✅
    fc-batch-2 07:49:54 ✅
    fc-batch-3 07:50:01 ✅
    fc-batch-4 07:50:09 ❌ dropped
    fc-batch-5 07:50:15 ✅
    fc-batch-6 07:50:22 ✅

    Image-gen batch (3 agents):

    Agent Completed Notification
    img-batch-1 08:16:55 ✅
    img-batch-2 08:17:01 ❌ dropped
    img-batch-3 08:17:06 ✅

    All 13 agents completed successfully (stop_reason: "end_turn", output files written). The race between completeAgentTask and enqueuePendingNotification matches exactly — the dropped notifications were never delivered despite agents finishing.

    Impact

    Automated pipelines with no user present stall indefinitely waiting for notifications that never arrive. Current workaround: timeout-based file checkpoint after last received notification.

    See also: #45581 (our original report, closing as duplicate)

  2. github-actions commented on May 23, 2026

    @github-actions

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

  3. github-actions commented on Jul 13, 2026

    @github-actions

    This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.

  4. locked as resolved and limited conversation to collaborators on Jul 13, 2026
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:agentsbugSomething isn't workinghas reproHas detailed reproduction stepsstaleIssue is inactive

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions