anthropics / anthropics/claude-code

remote-control session permanently 401s after token refresh fails; /login cannot reach the process

Open
#95,262 1 comment 0 reactions 0 assignees View on GitHub
area:auth bug has repro platform:macos
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

### What happened

A long-running session spawned by `claude remote-control` caches its OAuth credentials at process start. Once the in-memory access token expires and the refresh attempt fails, that session returns a 401 forever and there is no way to re-authenticate it in place.

The session transcript records, on every subsequent message:

```
"subtype": "api_error", "status": 401,
"formatted": "401 OAuth access token has expired. Re-authenticate to continue."
"retryAttempt": 1, "maxRetries": 10
```

The client UI then shows a banner telling the user to sign in again on their computer.

### Why it is not recoverable

Two obvious recovery paths both fail:

1. **`claude /login` in a separate terminal** — this succeeds and updates the credential store (I verified the new access token and a refresh token ~29 days out). The already-running session process never re-reads it. 401s continued 2 and 3 minutes *after* a successful login.

2. **Typing `/login` into the session's own input box on claude.ai** — never reaches the process. The transcript shows no `/login` being received; while the "sign in again" banner is displayed, the UI appears to re-send the previously pending message instead of the new input. So the one slash command that could fix the session cannot be delivered to it.

The session process has no TTY (it is a child of `~/.claude/remote/srv//server --serve`, itself under launchd), so there is no terminal route to it either.

### Only workaround found

Kill the remote-control agent and let launchd restart it. This works — the new agent picks up the current credentials — but **every bridged `claude.ai/code/session_*` URL is destroyed**, because new sessions get new IDs. In my case 14 sessions were lost this way; their transcripts survived on disk and had to be reopened individually with `claude --resume `.

### Impact

This is the normal remote workflow: work against the Mac from another machine (SSH / browser), come back later, continue. Instead, every bridged session becomes permanently unusable and the session URLs are lost. Transcripts are not lost, but the shared/bridged sessions are.

### Steps to reproduce

1. Run `claude remote-control` (launchd agent) and open a session from claude.ai.
2. Leave it running long enough for the access token to expire and the refresh to fail (in my case the refresh token was itself near expiry).
3. Send a message → `401 OAuth access token has expired`.
4. Run `claude /login` in a terminal; confirm the credential store is updated.
5. Send a message in that session again → still 401.
6. Type `/login` in that session's input box → not delivered; still 401.

### Expected

- Re-read the credential store when a request fails with 401 `authentication_error`, instead of retrying the same dead token 10 times.
- At minimum, accept `/login` typed into a bridged session's input box, and preserve the session/bridge URL across a re-authentication.

### Environment

- macOS 15.6 (Darwin 25.6.0), Apple Silicon
- Claude Code 2.1.268 (CLI), remote `ccd-cli` 2.1.271, desktop-bundled claude-code 2.1.270
- Claude Max subscription, OAuth login (not API key)
- Credentials in macOS Keychain; `~/.claude/.credentials.json` was present but empty (`accessToken: ""`, `expiresAt: 0`) — possibly related, as the failing processes are the `remote/ccd-cli` ones.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the `claude remote-control` launchd agent at `~/.claude/remote/srv//server --serve` and the `/login` command path. Reproduce the expired-token 401 after `claude /login` updates the credential store, then verify that re-authentication restores the existing bridged session and preserves its URL without restarting the agent.

Written by the indexing model from the issue text.

Assessment

Tech stack
macos, python
Domain
authentication, backend, cli
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.