[BUG] Desktop app (macOS): scheduled sessions with background tasks leak harness processes indefinitely (task-notification race, follow-up to #39632)

Status Open
Reported on v2.1.221
Maintainer reply None cached
Activity 0 comments · opened Aug 13, 2026

Summary

On the macOS desktop app, every scheduled/automated session that uses background work (background Bash, Monitor, subagents) leaves its harness process alive indefinitely after its last turn completes. The processes accumulate daily. The transcripts of every affected session end with queue-operation: enqueue entries carrying <task-notification> payloads that are never consumed, which matches the priority="later" race documented in #39632 (closed as not planned, now locked — hence this new report with fresh reproduction data).

Related earlier reports, all closed and locked: #18752, #21305, #45507, #51860.

Environment

  • Claude Code 2.1.221 inside the macOS desktop app (Darwin 25.5.0)
  • Sessions launched by the app's scheduler (entrypoint claude-desktop)
  • Harness binary: ~/Library/Application Support/Claude/claude-code/2.1.221/claude.app/Contents/MacOS/claude, wrapped by the Helpers/disclaimer helper

Observed behavior (2026-08-13, reproduced across two consecutive days)

  1. Six orphaned harness processes accumulated in a single day from scheduled sessions; processes from the previous day's runs were still alive 24 hours later and had to be killed manually.
  2. Each affected session's transcript ends with enqueued <task-notification> entries that never woke the idle session.
  3. The app's session list (list_sessions) reports these sessions as not running while the process is still alive. The flag also disagrees in the other direction: one session was reported not running while its transcript showed tool calls still being executed minutes later. App-side state and process state cannot be reconciled from either side.
  4. Cost per leaked instance is roughly 0.5 GB RSS (150–450 MB harness plus ~300 MB of stdio MCP server children), with idle TCP connections to the API kept open.
  5. SIGTERM is ignored by the stuck processes; SIGKILL works. After a kill, the app may relaunch a harness for the same session to deliver the still-queued notification, so the process reappears until the queue drains.

Steps to reproduce

  1. In the macOS desktop app, create a scheduled task whose session spawns background work (a background Bash command or a subagent) and ends its turn before the background work completes.
  2. Let the scheduled run finish; wait for the background task to complete.
  3. Compare pgrep -fl "claude.app/Contents/MacOS/claude" with the app's session list: the harness process for the finished session is still alive while the session is reported as not running, and the session transcript ends with an unconsumed enqueued <task-notification>.

Expected behavior

The harness process exits once the session's turn is complete and queued notifications are either delivered or discarded; the app's running state matches the process state.

Why re-triage matters

Scheduled and automated sessions make background tasks the common case rather than the exception. On any setup with daily scheduled runs the leak compounds every day (RAM, stale MCP configs held in memory, open API connections), and the only workaround is a manual SIGKILL sweep.

View original on GitHub ↗