Regression (2.1.195 → 2.1.196): background daemon transient-spawn fails over SSH on macOS — "Could not switch to audit session: Operation not permitted"
Regression (2.1.195 → 2.1.196): background daemon transient-spawn fails over SSH on macOS — "Could not switch to audit session: Operation not permitted"
Environment
- macOS, Apple Silicon
- Running over SSH (VS Code Remote-SSH;
sshd … @notty,SSH_CONNECTIONset) - Regression introduced in 2.1.196; still present in 2.1.197. Last-good: 2.1.195.
Symptom
With no daemon already running, the REPL session switcher / background agents cannot cold-spawn the background daemon from an SSH session — it hangs, then reports:
Couldn't reach the background service (not running) — run 'claude daemon status'
claude -r and foreground interactive sessions are unaffected (they never spawn the daemon). The bug is masked whenever a daemon is already running, because SSH clients just attach to the existing one instead of cold-spawning. It only surfaces on the first cold spawn from an SSH session after the daemon idle-exits.
Root cause
The fatal error is only visible in the client stderr (the daemon dies before it can open ~/.claude/daemon.log), so it must be captured with --debug-file:
[ERROR] daemon: transient spawn stderr:
Could not switch to audit session 0x…: 1: Operation not permitted
[WARN] background spawn failed: Couldn't reach the background service (not running)
macOS denies the audit-session switch in an SSH notty context. The client-side daemon-spawn code appears to have changed between 2.1.195 and 2.1.196 to require this audit-session join.
Deterministic A/B repro
Over SSH. Force a transient spawn with --bg, running claude daemon stop --any before each run so there is no pre-existing daemon to attach to. Invoke specific versions directly (the version entry under ~/.local/share/claude/versions/<v> is the executable itself, not a directory):
claude daemon stop --any
~/.local/share/claude/versions/<version> --bg -p "hi" --debug-file /tmp/x.debug
Results, repeated A/B/A/B, every run starting from daemon status = not running:
| Client version | audit-session error | result |
| -------------- | ------------------- | -------------------------------------------------------------------- |
| 2.1.195 | none | ✓ backgrounded; daemon log shows daemon start … origin=transient |
| 2.1.196 | yes | ✗ Couldn't reach the background service |
| 2.1.197 | yes | ✗ Couldn't reach the background service |
The daemon binary launched is identical (the 2.1.197 default) in all three cases — only the spawning client version differs. This isolates the regression to the client-side daemon-spawn path between 2.1.195 → 2.1.196.
Workarounds
- Keep a daemon alive so SSH clients attach instead of cold-spawning:
claude daemon runin a spare terminal/tmux (foreground origin works fine over SSH), or seed the daemon once from a GUI-session launch / the Desktop app. - Pin to 2.1.195.
Asks
- Restore ≤2.1.195 spawn behavior over SSH — don't require an audit-session switch for the transient daemon spawn, or fall back gracefully when it's denied.
- Fail fast with an accurate message instead of a long hang followed by the misleading "background service (not running)"; surface the underlying
Operation not permittedto the user, not just to--debug-file.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗