[BUG] Headless `--output-format stream-json` sessions spawned by desktop-app scheduled tasks (with Task subagent fan-out) fail to retire after completion

Resolved 💬 2 comments Opened Jun 10, 2026 by ReyMayson Closed Jun 14, 2026

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?

Scheduled headless claude --output-format stream-json sessions launched inside the Claude desktop app (Cowork scheduled-tasks), several of which fan out to Task subagents, finish their work but fail to exit. They accumulate as idle (<2% CPU) live processes parented to /Applications/Claude.app until the desktop app is restarted.

Observed signature:

| Attribute | Observed value |
| --- | --- |
| Build | 2.1.165 (desktop app; observed 2026-06-10 ~12:00) |
| Count at observation | 43 idle/hung claude sessions |
| Parent process | /Applications/Claude.app (every one — decisive: NOT launchd-parented) |
| Invocation form | headless --output-format stream-json |
| Subagent fan-out | several sessions spawn Task subagents (e.g. digest suite = 4 curators; nightly = per-item) |
| CPU at observation | all <1.6% (work finished — not hung mid-work) |
| Oldest session age | ~1 day 11 hours |
| Spread | ≥4 distinct fire cadences (the set of live Cowork scheduled-task producers) |
| Clears on | a Claude-for-macOS restart (symptom clear, not a fix) |
| Re-accumulates | yes — the pile re-forms between restarts (grows unbounded) |

Environment note: these sessions run on the desktop app's interactive credential (no ANTHROPIC_API_KEY is set for them, by deliberate cost/ToS decision). A session that finishes work but blocks on a credential refresh / permission prompt / stdin read at shutdown would present exactly as "idle, alive, not exiting." We flag this as a plausible contributing cause at the shutdown boundary, not a competing root cause. No literal credential value is reproduced here — only the category and that the sessions authenticate via the desktop app's interactive session.

What we already know is not the cause:

  • Not our LaunchAgent. Our launchd-scheduled shell automation spawns no claude processes on the affected paths. A launchd-spawned process is a child of launchd, not of Claude.app. Every one of the 43 was a Claude.app-child, so every one was a Cowork-runtime session. Parentage is decisive.
  • Not the plugin .in_use lock GC. That is a separate, by-design lazy-GC surface (tightened to a daily sweep in 2.1.169); unrelated to session-process retirement.

Why we believe this is never-covered upstream (changelog-diff analysis):

The changelog entries below are from the upstream Claude Code CHANGELOG as pulled live on 2026-06-10; version range surveyed 2.1.153–2.1.170. We searched 2.1.161–2.1.170 for session-teardown / idle-reap / subagent-retire / stream-json entries. The teardown-fix lineage clusters on two adjacent-but-distinct surfaces, neither of which is the headless stream-json Cowork-with-Task-fanout path:

Surface (a) — claude agents interactive background sessions:

  • 2.1.154 — "background sessions stuck ... not retiring after the idle grace period"
  • 2.1.157 — "completed sessions not retiring when an idle subagent was still parked or had leaked a backgrounded shell"
  • 2.1.160 — "Improved background-session teardown (claude rm/stop, idle reap) to send SIGTERM ... before SIGKILL so cleanup handlers run"
  • 2.1.169 — "made the child-process SIGTERM→SIGKILL window configurable (default unchanged at 5s)"

Surface (b) — claude -p foreground headless:

  • 2.1.161–2.1.163 window — "completed subagents getting stuck showing as running when an error occurs while finalizing their result"
  • 2.1.161–2.1.163 window — "claude -p hanging forever after its final result when a backgrounded command never exits — background shells are now stopped ~5s after the result once stdin closes"

Closest single match, and why it does not cover us: 2.1.153 — "CLI could fail to exit when stdin was closed without EOF in stream-json mode, leaving a stale session marker behind." This is stream-json, but it (i) predates the 2.1.160 baseline our earlier analysis was grounded on, and (ii) is single-session — it does not address the Cowork-runtime session whose Task subagent fan-out leaves the parent unable to retire.

Verdict (confidence: high): No entry in 2.1.161–2.1.170 names the headless --output-format stream-json Cowork-spawned session with multi-Task subagent fan-out failing to retire. No teardown fix was withdrawn, so this is not a regression — the fix class was acknowledged upstream but this specific spawn path was never covered.

What Should Happen?

Extend the session-teardown / idle-reap path (the 2.1.157/2.1.160 fix family) to cover headless --output-format stream-json sessions spawned inside the desktop-app (Cowork) runtime, including the case where a Task subagent's completion leaves the parent session unable to retire.

These sessions should exit cleanly when their work is done rather than accumulating until a desktop-app restart.

Error Messages/Logs

No crash/error log is required to observe the issue. The signature is visible in process state: completed sessions remain as idle (<2% CPU) live `claude` processes parented to `/Applications/Claude.app`.

The reproduced processes carry only CLI invocation flags (`--output-format stream-json`, model/tool flags); no user prompt content, file paths, or account data is required to reproduce or is included in this report.

Steps to Reproduce

  1. On macOS desktop Claude, register a scheduled-task (Cowork) that launches a headless claude --output-format stream-json session which dispatches multiple Task subagents and completes. This is the configuration observed to fail: e.g. a digest suite session fanning out to 4 subagents.
  2. Let it fire repeatedly across several cadences over >24h without restarting the desktop app.
  3. Observe via ps that completed sessions remain as idle (<2% CPU) claude processes parented to Claude.app, oldest aging past the session's expected runtime, count growing monotonically.
  4. Restart the desktop app. The process pile clears, confirming they are retire-failures, not active work.

Open question for Anthropic: is a single Task subagent sufficient to trigger the retire failure, or is multi-subagent fan-out required? Our observed evidence is exclusively multi-subagent; we have not bisected the minimal trigger down to one subagent. If your teardown path treats single- and multi-subagent completion differently, the answer may narrow where the fix belongs.

Claude Model

Other

Is this a regression?

No, this never worked

Last Working Version

N/A - not believed to be a regression

Claude Code Version

2.1.165 (Claude desktop app / Claude Code; observed 2026-06-10)

Platform

Other

Operating System

macOS

Terminal/Shell

Other

Additional Information

Observed on Claude desktop app / Claude Code build 2.1.165 on macOS, on 2026-06-10 around 12:00. Changelog range surveyed: 2.1.153–2.1.170.

This appears to be a specific uncovered spawn path rather than a regression: headless --output-format stream-json sessions spawned inside the desktop-app Cowork runtime, with Task subagent fan-out, failing to retire after completion.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗