anthropics / anthropics/claude-code
`claude remote-control` server exits on 401 after exactly 24h — does not refresh its OAuth access token, killing every attached session
- 主要言語
- Python
- スター
- 145k
- フォーク
- 23.1k
- PR マージ指標
- PR 指標を取得中
説明
### Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
**Version:** 2.1.233 (npm global) · macOS 15 (Darwin 25.6.0) · Apple Silicon Mac mini · Max subscription
## Summary
A long-running `claude remote-control` server dies exactly 24 hours after the process starts, with a 401 "OAuth access token has expired". It does not attempt a refresh, and it does not re-read the refreshed credentials that are already on disk. Every Remote Control session attached to that server is orphaned the instant it exits — the phone/web client shows the environment as gone, and in-flight work (running subagents, uncommitted edits) is lost.
The 24h figure is not approximate. Two independently-started servers on the same machine:
| Server | Started | Died | Lifetime |
|---|---|---|---|
| rc-claude | Aug 18 13:23:03 | Aug 19 13:23:09 | 24h 00m 06s |
| rc-hardware / rc-software / rc-workflows | Aug 18 13:01:51 | Aug 19 13:01:53 | 24h 00m 02s |
The second row is three servers dying in the same second, having been launched in the same second.
## Exact error
Captured from the tmux pane at the moment of exit (process exit status 0):
```
[13:23:09] Error: Poll: Authentication failed (401): OAuth access token has expired.
Re-authenticate to continue.. Remote Control is only available with claude.ai
subscriptions. Please use `/login` to sign in with your claude.ai account.
```
Note the doubled period in `continue..` — likely two message strings concatenated.
## The credentials on disk were valid
This is the key point: the machine's auth was fine. At the moment the server died holding an "expired" token, `~/.claude/.credentials.json` contained:
```
claudeAiOauth.expiresAt = 2026-08-19 19:30:38 (6h in the future)
claudeAiOauth.refreshTokenExpiresAt = 2026-09-14 06:51:40 (26 days in the future)
file mtime = 2026-08-19 11:30:38 (refreshed 2h earlier)
```
So another `claude` process had already refreshed the token successfully. The remote-control server appears to hold the access token it obtained at startup in memory for its whole life, never refreshing it and never re-reading the file. `/login` is not the fix — restarting the process is, which is why "run `claude` again" appeared to fix earlier incidents.
## Expected
The server refreshes its access token before expiry (the refresh token is valid for weeks) and keeps serving. A 401 on a poll should trigger a refresh-and-retry, not process exit.
## Actual
Process exits with status 0. Every attached session is orphaned with no warning, at a time of day determined by whenever the server last happened to start.
## Impact
The exit time inherits the server's start time, so the failure lands mid-workday for anyone whose server was last restarted mid-workday. In our case it landed at 13:01 on two consecutive days, twice killing active sessions with running subagents. Because it presents as "the environment no longer exists" on the client, it reads as a network or account problem — we chased a network-outage theory and a watchdog theory for two days before catching the actual error, since the pane is erased on exit.
## Reproduce
1. Start `claude remote-control --name X` on a machine and leave it running.
2. Attach one or more sessions from the mobile/web client.
3. Wait 24 hours (no interaction needed; it dies whether idle or busy — our idle server and our 3-session server both died at their own 24h marks).
4. The server exits with the 401 above; attached sessions are unrecoverable in place.
## Why I think this is distinct from the RC credential path already reported
#78453 shows the RC client fetching *remote credentials* from a `/bridge` endpoint at session-creation time — i.e. Remote Control mints and holds a credential of its own rather than using the account access token directly. That fits what I see: my account access token rotates on a several-hour cycle (the file showed an 8h validity window and had been refreshed 2h before the crash), yet the server survived a clean 24h from process start, twice. So the expiring thing looks like an RC-specific credential obtained once at startup and never renewed for the life of the process.
## Related issues
Same root class (token refresh in long-lived processes), but none covers a remote-control *server* exiting at its own 24h mark:
- #87271 — desktop app: OAuth refresh is reactive-only, surfacing on every ~2h rotation. Same underlying "refresh is not proactive" theme, different surface and different interval.
- #82217 — OAuth expiration not propagated to active subagent requests; long-running sessions "all broke overnight" with simultaneous 401s. Closest in spirit; that report is about subagent requests inside a session, not the RC server process exiting.
- #83958 — `claude remote-control` 401 at the **Registration** phase with "Missing Authorization header". Different phase, different message, and it fails at startup rather than after 24h of healthy service.
- #78453 — 401 at the `/bridge` endpoint after session creation. Different phase; cited above as evidence for the separate-RC-credential hypothesis.
## Suggested fixes
1. Refresh the OAuth access token proactively in the remote-control server, or on a 401 retry the request once after refreshing.
2. Failing that, re-read `~/.claude/.credentials.json` on 401 before giving up — in our case a valid token was sitting there.
3. Do not exit 0 on a fatal auth error, and write the reason to stderr as well as the TUI. The alternate-screen restore wipes the message on exit, which is what made this undiagnosable for two days.
コントリビューションガイド
このリポジトリのコントリビューションガイドは索引されていません
調査の方向性
Start at the `claude remote-control --name X` entry point and trace its polling and authentication flow, comparing it with the refresh behavior for `~/.claude/.credentials.json`. The `/bridge` endpoint and related issue #78453 provide context for the RC-specific credential path. Done means a long-running server refreshes or reloads valid credentials after expiry, retries the 401 appropriately, and keeps attached sessions alive.
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- python
- 領域
- authentication, backend, cli
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 活発
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 45/100