Daemon 'upgrade storms': stale session's updater reinstalls in a loop; daemon self-restarts on mtime; stale desktop CLI seizes namespace

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

Summary

A machine with (1) one long-running interactive session on an older binary and (2) the npm-wrapper install experienced four multi-hour "upgrade storms" (Jul 22, 24, 25, Aug 3) in which the background-session daemon self-restarted 30-83 times per storm, killing attached PTYs each cycle. Three compounding behaviors, each arguably a defect on its own:

1. The auto-updater of a long-running session loops forever

A session started days ago keeps its loaded version. Its in-process updater compares that version against npm view @anthropic-ai/claude-code@latest, sees a mismatch, and runs npm install --global @anthropic-ai/claude-code@<latest> - which can never change the running process's version, so the same session reinstalls on every check, indefinitely. Observed: a session started Aug 2 (2:11PM) drove installs in activity-correlated bursts through Aug 3 evening (~/.npm/_logs: repeated "install" "--global" "@anthropic-ai/claude-code@2.1.221" while 2.1.221 was already installed AND already latest).

Suggested fix: the updater should no-op when the installed target version already equals latest (check the destination, not the running process), or mark "already reinstalled once this process lifetime".

2. The daemon self-restarts on binary mtime change - amplifying any reinstall into a storm

daemon.log: [supervisor] binary at .../claude-code/bin/claude.exe changed (mtime changed) - self-restarting for upgrade. The npm wrapper's postinstall rewrites bin/claude.exe on every install, including a same-version reinstall, so each no-op install murders the daemon. 30 restarts in 2.5h (Aug 3), 39/47/83 in the July storms. Attached PTY sessions die with it (see 4).

Suggested fix: compare content hash or version, not mtime, before self-restarting.

3. A stale desktop-managed CLI seizes the daemon namespace

With the desktop app's managed store stale (~/.local/share/claude/versions/{2.1.150,2.1.156} present; the ~/.local/bin/claude launcher long gone), daemon-less moments during storms led to 2.1.156 becoming the daemon for a namespace whose every other process was 2.1.221 (daemon.log: daemon start ... version=2.1.156, plus binary identity unresolvable at ~/.local/bin/claude; upgrade polling disabled). A months-old binary silently hosting current sessions is a correctness and security hazard.

Suggested fix: a daemon whose version differs from the invoking CLI's should refuse or hand off; the version store shouldn't be consulted when the npm install is canonical (installMethod: "global").

4. PTY orphan watchdog kills attached sessions during storms

During daemon churn, an interactive session attached via the daemon's PTY host was killed by the orphan watchdog (jobs-archive exit-cause: ptyhost_orphan_watchdog), leaving a wedged job record ("working" + phantom in-flight shell) that required manual archive + resume.

Environment

macOS 15 (Darwin 24.6.0), Claude Code 2.1.221 via npm global (fnm-managed node v24.16.0), desktop app also installed. Heavy multi-session use (10-20 concurrent background sessions).

View original on GitHub ↗

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