[BUG] Headless `--output-format stream-json` sessions spawned by desktop-app scheduled tasks (with Task subagent fan-out) fail to retire after completion
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 noclaudeprocesses on the affected paths. A launchd-spawned process is a child oflaunchd, not ofClaude.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_uselock 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 -phanging 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
- On macOS desktop Claude, register a scheduled-task (Cowork) that launches a headless
claude --output-format stream-jsonsession which dispatches multipleTasksubagents and completes. This is the configuration observed to fail: e.g. a digest suite session fanning out to 4 subagents. - Let it fire repeatedly across several cadences over >24h without restarting the desktop app.
- Observe via
psthat completed sessions remain as idle (<2% CPU)claudeprocesses parented toClaude.app, oldest aging past the session's expected runtime, count growing monotonically. - 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.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗