anthropics / anthropics/claude-code

Remote Control archives live sessions after a transient network give-up, bypassing the preserve-on-shutdown path

Đang mở
#92,194 0 bình luận 0 reaction 0 người được giao Xem trên GitHub
area:networking bug has repro platform:macos
Ngôn ngữ chính
Python
Star
145k
Fork
23.1k
Chỉ số merge pull request
Chỉ số pull request đang chờ

Mô tả

### Preflight Checklist

- [x] I have searched existing issues. Closely related reports exist (#71873, #90387, #91627) — see **Related Issues**. This report supplies the mechanism #71873 was closed for lacking.
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code

### What's Wrong?

`claude remote-control` treats a temporary loss of connectivity as a fatal error.
On that exit path it archives every active session and deregisters the
environment. The sessions were healthy and the user never archived them, but
they show up archived in the mobile app and on claude.ai/code.

The shutdown path already has a preserve branch that skips archiving. A
connection give-up sets the same fatal flag as a genuinely unrecoverable error,
so it bypasses that branch.

From the strings in the shipped bundle (`2.1.260`):

```js
// Archiving is skipped only when preserveOnShutdown is set AND the fatal flag is clear
if (e.preserveOnShutdown && !ft) {
// "Environment preserved. Restart `claude remote-control` to reconnect…"
return;
}
if ($t.size > 0)
await Promise.allSettled([...$t].map((v) => d.archiveSession(...)));
await d.deregisterEnvironment(r);
```

```js
// …and the plain connection give-up sets that flag, alongside real fatal errors
if (j >= w.connGiveUpMs) {
o.logError(`Server unreachable for ${...} minutes, giving up.`);
...
ft = !0;
break;
}
```

The budget is `connGiveUpMs: 600000` against a retry backoff capped at
`connCapMs: 120000`. Ten minutes of budget with a two-minute cap allows only
about six probes, so a window of recovery can be missed. There is no environment
variable or flag to raise the threshold.

### What Should Happen?

A lost network connection is not an unrecoverable environment. The give-up path
should take the existing preserve branch and leave sessions alone, so they are
still there when connectivity returns. Archiving should stay reserved for
teardowns that genuinely end the environment.

### Error Messages/Logs

From `~/.claude/logs/` on my machine, ANSI stripped. The bridge runs under a
service wrapper that restarts it.

**An active session killed and archived directly by the give-up:**

```
[19:10:42] Connection error, retrying in 149.9s (225s elapsed): timeout of 10000ms exceeded
[19:13:32] Connection error, retrying in 137.6s (395s elapsed): timeout of 10000ms exceeded
[19:16:10] Connection error, retrying in 113.1s (552s elapsed): timeout of 10000ms exceeded
[19:18:23] Error: Server unreachable for 11 minutes, giving up.
[19:18:23] Shutting down 1 active session(s)…
[19:18:23] Session failed: Process exited with error cse_01UzhdL4Gt8U9hGgyTqFjVWh
[19:18:28] Failed to archive session session_01UzhdL4Gt8U9hGgyTqFjVWh: not found
[19:18:28] Failed to archive session cse_01UzhdL4Gt8U9hGgyTqFjVWh: not found
[19:18:30] Environment deregistered.
```

**An earlier occurrence where the give-up was demonstrably premature.** The
bridge declared the server unreachable, the wrapper restarted it, and the new
process connected on its first attempt:

```
[15:59:56] Error: Server unreachable for 11 minutes, giving up.
[15:59:56] Failed to deregister environment: Remote Control is only available with claude.ai subscriptions. Please use `/login` to sign in with your claude.ai account.
[15:59:56] Environment offline.
Remote Control v2.1.220
Spawn mode: worktree
...
·✔︎· Connected · agent-os
```

Note the deregister failed with an auth message rather than a network one, which
suggests these "connection errors" may in part be failed token refreshes
surfacing as timeouts. I have not confirmed that, but it matches the trigger
suspected in #71873.

In both captured cases the archive call returned `not found`, because the server
had already reaped the environment. So what I can prove directly is that the
bridge *attempts* to archive healthy sessions after a network blip. When the
server still holds the session — the expected case for a shorter outage — that
call succeeds, which is the user-visible symptom reported in #71873 and #90387.

### Steps to Reproduce

1. Start a server: `claude remote-control --spawn worktree --capacity 4`
2. Open the printed `claude.ai/code?environment=…` URL and send a message, so a
session is active.
3. Cut the machine's network connection — or block the API host — and keep the
machine awake. Do not let it sleep; the poll loop detects a sleep gap and
resets its error budget, which masks the bug.
4. Wait past the ten-minute `connGiveUpMs` budget. Observe
`Server unreachable for 11 minutes, giving up.` followed by
`Shutting down N active session(s)…`, archive calls, and
`Environment deregistered.`
5. Restore the network. The session is gone from the default list.

### Related Issues

Filed separately because the trigger and code path differ from each of these.

- **#71873** (closed, not planned) — "Remote Control session auto-archives while
still active," which that reporter attributed to a connection / token-refresh
blip without being able to point at a mechanism. This report is that
mechanism. Worth reconsidering alongside it.
- **#90387** (open) — sessions archived at teardown and never unarchived,
invisible on mobile. Same destination, different trigger (desktop app
auto-update). That report explicitly excludes server mode: "Sessions on a
separate machine running `claude remote-control` in server mode are unaffected."
This is a server-mode path that reaches the same outcome. The unarchive-never-
propagates half of that report applies here too and makes the loss permanent.
- **#91627** (open) — the archive call is issued against a constructed
`session_` id that the server has no record of. I see the same thing, and can
add one data point: in `--spawn worktree` mode the bridge issues **two**
archive calls for one session, once as `session_` and once as
`cse_` (visible in the log above, same ULID, five seconds apart). The
shutdown set is a `Set` of id strings, so it dedupes by exact match, while the
id-comparison helper used on the line above it already handles both forms.
That belongs to #91627; I mention it only because it appears in my logs.

### Claude Model

Opus

### Is this a regression?

I don't know

### Claude Code Version

2.1.260 (Claude Code), native install. Also observed on 2.1.220.

### Platform

claude.ai subscription via `/login`, not an API key

### Operating System

macOS 26.5.2, arm64

### Additional Information

- Spawn mode `worktree`, capacity 4, bridge run under a service wrapper that
restarts it on exit.
- Operational note for anyone hitting this: system sleep is safer than a flaky
connection. The poll loop detects a sleep gap and resets its error budget, so
closing the lid is fine. A connection that keeps timing out while the machine
stays awake burns the budget through to the archive.

Hướng dẫn đóng góp

Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này

Hướng nghiên cứu

Start by reproducing with `claude remote-control --spawn worktree --capacity 4`, an active `claude.ai/code?environment=…` session, and a blocked network until the `connGiveUpMs` give-up log appears. Then trace the remote-control shutdown path around `preserveOnShutdown`, the fatal flag, archive calls, and environment deregistration. Done means transient give-up preserves active sessions while genuine teardown still archives as intended.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Đánh giá

Công nghệ
javascript, python
Lĩnh vực
backend, cli
Loại issue
Lỗi
Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức độ hoạt động
Sôi nổi
Độ rõ ràng
Khá rõ ràng
Mức phù hợp với người mới
48/100

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.