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

Ouverte
#88,054 3 commentaires 0 réactions 0 personnes assignées Voir sur GitHub
area:auth bug has repro platform:macos
Langage dominant
Python
Étoiles
145k
Forks
23.1k
Métriques de merge des PR
Métriques de PR en attente

Description

### 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.

Guide de contribution

Aucun guide de contribution indexé pour ce dépôt

Piste de recherche

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.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
authentication, backend, cli
Type d'issue
Bug
Difficulté
4/5
Temps estimé
3-5 jours
Activité
Active
Clarté
Plutôt claire
Accessibilité débutants
45/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.