[BUG] Remote Control (--spawn worktree) self-restarts in a rapid loop, eventually hits systemd start-limit-hit and dies
What's Wrong?
claude remote-control --spawn worktree (run as a systemd --user service, Type=simple, Restart=on-failure) enters a rapid restart loop that is not caused by a process crash. The main process exits cleanly (status=0/SUCCESS) after receiving SIGTERM, and the debug log shows this is a self-initiated graceful shutdown ([DEBUG] [bridge:shutdown] SIGTERM received, shutting down → [DEBUG] [bridge:shutdown] Skipping archive+deregister to allow resume in one failure mode, or a clean environment deregister in another), not an unhandled exception.
Two distinct failure signatures were observed on the same host, back to back, over ~2.5 hours:
Signature A — poisoned queued work item (before manual cleanup):
A work item polled from the backend (GET .../work/poll) referenced a session created 16 days earlier ("created_at":"2026-08-02T00:25:47.623735Z", "acknowledged_at":null). Every time the parent process spawned a child to resume it (--resume=https://api.anthropic.com/v1/code/sessions/cse_...), the child took ~1s to boot (skills/MCP/hooks load), then failed with:
[ERROR] CCRClient: Epoch mismatch (409, reason=session_not_active), shutting down
exiting with exit_code=1 every single time. The parent logged [ERROR] Bridge session failed: Process exited with error, called /work/.../stop, went idle, and ~18–25s later the entire systemd unit received SIGTERM and restarted — re-polling the same stale work item on the next boot. This repeated for hours (11+ restarts observed in one ~1h window), because the backend kept re-serving the same never-expiring queued item.
Signature B — after deleting the stuck session/environment from claude.ai/code (all sessions in the environment removed "to be safe"):
The epoch-mismatch error disappeared, but the service now registers a brand-new environment_id on every single restart (never reusing the value persisted in the local bridge-pointer.json), gets a clean GET .../work/poll -> 200 (no work, ...), and then within 1–52 seconds receives SIGTERM again, cleanly deregisters (DELETE /v1/environments/bridge/env_... -> 200), and restarts — registering yet another new environment. This repeated fast enough (down to ~10s between restarts) to trip systemd's own StartLimitBurst=3/StartLimitIntervalSec=120 circuit breaker, putting the unit into failed (Result: start-limit-hit) — i.e. Remote Control becomes permanently unreachable until a human manually runs systemctl --user reset-failed && systemctl --user start.
In both signatures, a full 30-minute D-Bus capture (busctl --user monitor org.freedesktop.systemd1) while the unit was masked (systemctl --user mask) showed zero StartUnit/RestartUnit calls against this unit from any external source — confirming the restart is not triggered by cron, a systemd timer, an external script, or a local watchdog. The only D-Bus activity referencing the unit was periodic UnitNew/UnitRemoved pairs from systemd --user itself (routine status-query bookkeeping, unrelated to starting anything). This strongly points to the claude remote-control process restarting itself (very likely via a self-issued systemctl --user restart or equivalent) as an internal recovery mechanism — but the debug log (via --debug-file) shows no error, warning, or log line explaining why it decides to do this before issuing the SIGTERM-to-self.
What Should Happen?
- A single stale/orphaned queued work item should not be re-served indefinitely by the backend poll endpoint after repeated failures — it should expire, get marked failed permanently, or be skipped after N failed attempts, instead of poisoning the whole environment's poll loop forever.
- The Remote Control server should not need to fully restart its own process (losing all other live worktree sessions in the process — this happened while other unrelated sessions were attached) to recover from one bad work item; it should isolate/skip the bad item and keep serving the rest of the queue.
- If a full self-restart really is the intended recovery strategy, it should back off (not restart every 10-90s) and should never restart so fast that it trips systemd's
StartLimitBurst, since that leaves Remote Control completely dead until manual intervention — the opposite of what a "resilience" restart is supposed to achieve. - The debug log (
--debug-file) should log the reason the process is telling itself to restart (currently it just showsSIGTERM received, shutting downwith no preceding explanation of why the shutdown was decided).
Error Messages/Logs
# Signature A (before session cleanup) — repeats every ~18-25s cycle:
2026-08-18T10:37:21.854Z [DEBUG] [bridge:init] Adopted session cse_01CmQ1VrvVPe7ZGiAZ2m7kz9 re-queued via bridge/reconnect
2026-08-18T10:37:22.508Z [DEBUG] [bridge:api] GET .../work/poll -> 200 workId=cse_01CmQ1VrvVPe7ZGiAZ2m7kz9 type=session sessionId=[REDACTED]
2026-08-18T10:37:22.508Z [DEBUG] [bridge:api] <<< {"id":"cse_01CmQ1VrvVPe7ZGiAZ2m7kz9", ... "created_at":"2026-08-02T00:25:47.623735Z","acknowledged_at":null, ...}
2026-08-18T10:37:23.349Z [DEBUG] [bridge:session] Spawning sessionId=[REDACTED] sdkUrl=https://api.anthropic.com/v1/code/sessions/cse_01CmQ1VrvVPe7ZGiAZ2m7kz9
# ... child process boots (~1s), loads skills/MCP/hooks, then:
2026-08-18T10:38:48.583Z [ERROR] CCRClient: Epoch mismatch (409, reason=session_not_active), shutting down
# parent:
2026-08-18T10:37:24.873Z [DEBUG] [bridge:session] sessionId=[REDACTED] failed exit_code=1 pid=1710773
2026-08-18T10:37:24.877Z [ERROR] Bridge session failed: Process exited with error
2026-08-18T10:37:25.582Z [DEBUG] [bridge:work] stopWork succeeded for workId=cse_01CmQ1VrvVPe7ZGiAZ2m7kz9 on attempt 1/3
# ~18s later, unprompted:
2026-08-18T10:37:43.139Z [DEBUG] [bridge:shutdown] SIGTERM received, shutting down
2026-08-18T10:37:43.140Z [DEBUG] [bridge:shutdown] Skipping archive+deregister to allow resume (env env_01Xq17zwDto2GMiTf9B1nkNu, spawnMode worktree)
# next boot re-polls the SAME workId=cse_01CmQ1VrvVPe7ZGiAZ2m7kz9, loop repeats.
# Signature B (after deleting the stuck session/environment from the app) — repeats every 1-90s:
2026-08-18T10:46:43.418Z [DEBUG] [bridge:api] POST /v1/environments/bridge -> 200 environment_id=env_01WcaErHgh5rK75s9JAwQ41D
2026-08-18T10:46:43.420Z [DEBUG] [bridge:work] Starting poll loop spawnMode=worktree maxSessions=32 environmentId=env_01WcaErHgh5rK75s9JAwQ41D
2026-08-18T10:46:43.773Z [DEBUG] [bridge:api] GET .../work/poll -> 200 (no work, 1 consecutive empty polls)
2026-08-18T10:47:35.279Z [DEBUG] [bridge:shutdown] SIGTERM received, shutting down
2026-08-18T10:47:35.290Z [DEBUG] [bridge:api] DELETE /v1/environments/bridge/env_01WcaErHgh5rK75s9JAwQ41D -> 200
2026-08-18T10:47:35.870Z [DEBUG] [bridge:shutdown] Environment deregistered, bridge offline
# immediately re-registers as a DIFFERENT environment_id (env_01Gkaw23GLnhZLhW9yFbeXbs), clean poll, SIGTERM again 4s later, repeat.
# ...eventually:
systemd[1639]: claude-remote-control.service: Start request repeated too quickly.
systemd[1639]: claude-remote-control.service: Failed with result 'start-limit-hit'.
(sessionId/session_id values were [REDACTED] by Claude Code's own debug logger; environment_id and workId values above are real, from this incident, and are safe to correlate against backend records for this account/org.)
Steps to Reproduce
I don't have a minimal, from-scratch reproduction — this was found on a long-running host, and Signature A specifically required an already-stale (16-day-old) queued work item to exist, which isn't something a fresh setup starts with. Rough steps that led to it:
- Run
claude remote-control --spawn worktree --name "<name>"as a long-lived systemd--userservice (Type=simple,Restart=on-failure,RestartSec=15). - Over time, a queued session/work item for that environment becomes stale/orphaned (in our case, ~16 days old,
acknowledged_atstuck atnull). - Observe the service restart repeatedly (every ~15s-10min) with the
CCRClient: Epoch mismatch (409, reason=session_not_active)error on the spawned child, and a parent-levelSIGTERM received, shutting down~18-25s after each failed spawn (Signature A). - From
claude.ai/code, open the environment (https://claude.ai/code?environment=env_...) and delete/archive all sessions shown for it, hoping to clear the stale item. - Restart the systemd service once, cleanly, with no other interference.
- Observe the restart interval shorten to 10-90s, with a brand-new
environment_idregistered on every restart and a cleanSIGTERMa few seconds after each successful (empty) poll (Signature B) — until systemd'sStartLimitBurst=3/120s trips and the service dies entirely (start-limit-hit), requiring a manualsystemctl --user reset-failed && systemctl --user startto recover.
A 30-minute D-Bus capture with the unit masked showed zero external StartUnit/RestartUnit calls, ruling out cron/timers/scripts/watchdogs as the trigger — the restart is self-initiated by the claude remote-control process.
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Claude Code Version
2.1.234 (Claude Code)
Platform
Anthropic API (OAuth via Claude subscription, claude /login — not a raw API key, Bedrock, or Vertex)
Operating System
Other Linux (Fedora, bare-metal, systemd --user service)
Terminal/Shell
Non-interactive/CI environment (runs as a systemd --user Type=simple service, not attached to any terminal)
Additional Information
- Full unit file (relevant parts):
``ini``
[Service]
Type=simple
ExecStart=%h/.local/bin/claude remote-control --spawn worktree --name "<name>"
Restart=on-failure
RestartSec=15
RestartMaxDelaySec=120
RestartSteps=4
StartLimitIntervalSec=120
StartLimitBurst=3
- Reproduced the "self-restart, not external" finding via:
systemctl --user mask claude-remote-control.service(after moving the real unit file aside and manually symlinking it to/dev/null, sincesystemctl maskrefused to overwrite an existing regular file), thenbusctl --user monitor org.freedesktop.systemd1for 30 minutes — zeroStartUnit/RestartUnitcalls against the unit; only routineUnitNew/UnitRemovedsignal pairs fromsystemd --useritself (unrelated status bookkeeping, confirmed not to trigger activation). - Tried two mitigations, neither stopped it: (1) deleting all sessions for the environment via the
claude.ai/codeweb UI (changed the failure signature from A to B but didn't stop the loop), (2) adding--no-create-session-in-dirtoExecStart(no effect). - While the loop is active, other unrelated, legitimately-attached worktree sessions in the same environment get disrupted too, since the entire Remote Control server process restarts, not just the one bad work item's child process.
- Happy to share the account/org UUID and machine ID privately with an Anthropic engineer if useful for looking up server-side records for this incident — omitted here since this report is public.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗