[Bug] Remote Control: stale environments cannot be deleted and ghost sessions cause permanent 404 errors
UPDATE: This is not a single stale session. After registering a brand-new environment,
the 404 returned on the next launch with a DIFFERENT session ID (cse_<REDACTED_2> vs the
original cse_<REDACTED_1>), on the same fresh environment. Sessions appear to be created
and then not found at worker-attach time, seconds later. Abandoning the environment does
not fix it — the new environment reproduces the failure and leaves an undeletable duplicate
row in the picker.
Remote Control: a ghost session can permanently 404 an environment, and stale environments can't be removed or told apart
Preflight Checklist
- [x] I have searched existing issues (this extends #50884 with a case where the missing delete affordance is not merely cosmetic)
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code (2.1.208)
---
What's Wrong?
Two problems that compound each other: a stale session can permanently break an environment, and there is no way to remove the environment you're then forced to abandon.
1. A ghost session 404s on every launch
Every claude remote-control start printed:
Error: CCR v2 worker registration failed for session cse_<REDACTED>:
Request failed with status code 404
Same session ID each time, reproducible across restarts, on the latest build, with claude doctor clean and no ANTHROPIC_* variables set. The environment's server-side session list contained a session that no longer existed, so the bridge tried to attach a worker to it on every start and failed.
The session ID is not stored locally — grep -rl "cse_<id>" ~/.claude ~/.claude.json returned nothing. Only the environment pin is local:
~/.claude/projects/<mangled-project-path>/bridge-pointer.json
Workaround: move bridge-pointer.json aside, forcing registration of a fresh environment. This clears the 404 — but the only escape from a bad environment is to abandon it, which strands the old one in the picker forever. Which leads to:
2. Stale environments cannot be removed or told apart
The picker now shows two identical, dead rows:
Remote Control
<Folder> 0 of 32 <Hostname>
<Folder> 0 of 32 <Hostname>
- No delete affordance in the CLI, web, or mobile app.
- Confirmed server-side: the rows appear at
claude.ai/codein a browser, not just in Desktop. Signing out and relaunching does not clear them. Nothing local references them. - Environments cannot be renamed. The label is derived from folder name + hostname, so two environments registered from the same directory are visually indistinguishable. The only tell between live and dead is the capacity count.
Contributing factor: Claude Desktop Code sessions also register Remote Control environments (via Settings → Claude Code → Enable remote control by default). This made the source hard to trace — ps aux | grep "claude remote-control" returned nothing while the rows persisted, because the registering processes were Desktop Code sessions, not a remote-control server.
---
Steps to Reproduce
- Run
claude remote-controlin a project directory. Note the environment ID. - Get the environment into a state where a session in its list no longer exists server-side. (In my case this happened on its own; I don't have a deterministic trigger.)
- Observe the 404 on every subsequent launch. It survives restarts and
claude update. - Move
~/.claude/projects/<project>/bridge-pointer.jsonaside and relaunch. - The 404 is gone, but the picker now permanently shows two identical rows for the same folder — one live, one dead — with no way to remove either.
---
Expected Behavior
- Orphaned sessions are reaped server-side, so a dead session ID cannot permanently 404 an otherwise healthy environment.
- The CLI can drop a bad session without abandoning the whole environment.
- Remote Control environments can be deleted from the web, mobile, and CLI.
- Environments can be named, or the picker surfaces the working directory, so two entries from the same folder are distinguishable.
- Sessions deregister gracefully on shutdown so rows don't strand.
---
Impact
The current best workaround is to ignore the environment picker entirely and navigate by environment URL. For anyone using Remote Control regularly, the picker fills with dead, identically-named entries and stops being usable.
---
Environment
- Claude Code: 2.1.208 (native install)
- Platform: macOS, darwin-arm64
- Shell: zsh
- Also installed: Claude Desktop (Code tab), remote control on by default//
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Confirming this on Claude Desktop 1.24012.9, macOS 26.6 (arm64), Max plan — still present, and with one new detail that I think matters for the "how do you tell live from dead" part.
The capacity counter is not live at all.
This issue and #78695 both note that the only tell between a live and a dead environment is the session count. That heuristic doesn't hold. In my case the picker shows:
I fully terminated Claude Desktop on the host machine and verified it was gone (
pgrep -x Claudeempty, noclaudeCLI installed on that machine at all). Both rows stayed, and the second one kept reading2 of 32— two sessions attributed to a host with no process running. So a stale environment can show a non-zero count, and a count of zero doesn't mean the environment is the dead one either. There is currently no reliable way to tell them apart.Desktop-hosted equivalent of
bridge-pointer.jsonYour note that Desktop Code sessions register environments (and therefore
ps aux | grep "claude remote-control"finds nothing) was exactly right, and cost me a while to work out independently. For the Desktop path the local pin lives at:keyed by
<orgId>:<accountUuid>— no device component, so there is a singleenvironmentIdper app installation and it is overwritten in place. Same failure shape as movingbridge-pointer.jsonaside: the previous environment is never deregistered, and once the id is overwritten nothing local references it, so the app can't release it on quit either. Grepping all Claude state on two machines forenv_returns only this one file, holding only the current id.Also ruled out here, in case it saves anyone time
| Attempted | Result |
|---|---|
| Cmd+Q and relaunch | Both rows remain |
| Reboot the host machine | Both rows remain |
| Signing out of the account in Claude Desktop on the host | Both rows remain;
bridge-state.jsonuntouched, stillenabled: truewith the sameenvironmentId|| Settings → Claude Code → Enable remote control by default toggle |
bridge-state.jsonbyte-identical afterwards; the setting isn't persisted in the app'sconfig.jsoneither || Right-click the row in the picker | No context menu |
| claude.ai/code web picker | Same two rows; in the DOM they are bare
menuitemradioelements — no delete control, no submenu, nothing revealed on hover |One difference from #78695:
environmentIdstayed constant across several app restarts, so the Desktop path doesn't accumulate a new environment per launch. The orphan here appears to be a one-off leftover from an earlier reinstall or re-login, which I couldn't reproduce after the fact.Happy to supply environment ids and account details privately.
Additional datapoint — a slightly different flavor of the same server-side dangling reference: in our case it is the worker itself that keeps retrying a ghost session at every startup.
Setup:
claude remote-controlrunning 24/7 under systemd (Ubuntu Server 26.04). At every service start over the past week and a half (9 starts, currently on CLI 2.1.223), the worker logs:Always the same weeks-old session id. The environment itself re-registers fine and new sessions work, so this is harmless day-to-day — but the dangling reference survives CLI version updates, nothing in the CLI or the apps lists it, and so nothing can delete it.
Corroborates both halves of this issue: session records that go missing server-side while a client still holds a reference to them, and no cleanup path for the resulting ghost.
+1, hitting this too. Adding a clean repro in case it helps narrow the cause down — this one isn't from a directory-layout change, just plain restarts of the same server pointed at the same unchanged directory.
Setup: Windows 11, native claude.exe install, claude remote-control --spawn same-dir --capacity 4 launched by a Task Scheduler job (restarts the server on legitimate triggers — a local config fix, a network-outage self-heal check, etc. — so restarts happen periodically over the life of the setup, not just once).
Repro:
Start claude remote-control in a directory. Console prints an environment URL: https://claude.ai/code?environment=env_AAAAAAAA.
Stop the process, start it again in the exact same directory (no path change, no casing change, nothing).
Console prints a new ID: env_BBBBBBBB.
Repeat over a few days of normal restarts → mobile app (Devices → [device]) now lists the same local folder as N separate entries, one per historical restart, with no way to tell which is the live one.
Over three days of intermittent restarts I've got 4 entries for one folder now.
Ruled out on my end, in case it saves someone time:
Not a local .claude.json issue — that file only ever has one correct/trusted entry for the directory at any point in time.
Not an app-cache issue — reinstalling the mobile app wouldn't touch this, since the entries reflect live backend state, not something cached client-side.
No CLI escape hatch — claude remote-control --help has no list/revoke flag.
So it looks like every server start unconditionally mints a new environment registration rather than reusing/updating an existing one for the same directory — the duplicate-entries-on-layout-change case in the OP is one trigger, but a plain restart with nothing changed is enough on its own.
+1 on the OP's proposed fixes — either reuse-by-directory on restart, or a delete/revoke action (CLI or UI) so this doesn't grow unbounded on any setup where the server restarts periodically by design.