anthropics / anthropics/claude-code

[BUG] Desktop app: elevated-auth re-login navigates to /new and destroys the prompt being typed

Aperta
#88,824 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
area:auth area:desktop bug data-loss has repro platform:linux
Lingua principale
Python
Stelle
145k
Fork
23.1k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

### Summary

In the Claude desktop app, an unsaved prompt in the composer is destroyed when the app triggers a re-login. The user types a long prompt, the app then demands a login ("second login today"), and after logging back in the composer is empty and the conversation is gone. No draft, no undo, no recovery.

Log analysis shows this is **not** a plain session expiry. The base session key is still valid; the app fails a background **OAuth scope expansion** and reacts by doing a full top-level navigation of the main window to the login page with `returnTo=/new`. The `/new` is the actual data-loss bug: even a perfect login returns the user to a *new, empty* conversation instead of the one they were composing in.

### Root cause (from `~/.config/Claude/logs/main.log`)

Sequence, all within one second, during background startup sync (plugins / skills / CCD download) — i.e. triggered by the app, not by any user action:

```
15:42:14 [error] OAuth token refresh failed: status=400, response={"error": "invalid_grant", "error_description": "Refresh token expired"}
15:42:14 [info] [oauth-v2] refresh rejected (auth_error) for orgId=; tombstoning and falling through
15:42:14 [info] [oauth-v2] performing fresh /authorize for orgId=
15:42:15 [info] oauth failed: authorize returned 403
{"type":"error","error":{"type":"permission_error",
"message":"Session is not fresh enough to grant elevated access. Sign in again to continue.",
"details":{"error_code":"session_stale_relogin"}},
"request_id":"req_011CeHtEBsrjPEPfaS42Hg8R"}
15:42:15 [info] oauth authorize rejected with session_stale_relogin; sessionKey is valid but too old for the requested scope expansion
15:42:15 [warn] Cannot get base query config: oauth failed (session_stale_relogin)
```

The main window is then navigated to:

```
https://claude.ai/login?returnTo=%2Fnew%3Freason%3Delevated_auth%26auth_kind%3Dsession_stale_relogin%26org_uuid%3D&reauth=1&from=logout
```

Three separate problems in that one URL:

1. **`returnTo=/new`** — the return target is a brand-new conversation, so the composer is remounted empty. The conversation the user was actually in is not preserved.
2. **`from=logout`** — the app treats a *scope-expansion* failure as a logout, even though the log explicitly says `sessionKey is valid`. A still-valid session should not tear down the UI.
3. **Top-level navigation of the main window** instead of a popup/modal, which unmounts the composer and takes the typed text with it.

Recovery is logged 2m22s later (`15:44:39 [info] [oauth] clearing latched session_stale_relogin failures`) — so the user pays with their draft for a failure the app resolves on its own.

### Steps to reproduce

1. Log in to the desktop app; leave it running long enough for the OAuth refresh token to expire.
2. Start typing a long prompt in an existing conversation.
3. Let the app perform a background scope expansion (startup sync, plugin/skill sync, Cowork feature that needs elevated scope).
4. `/authorize` returns 403 `session_stale_relogin`; the login screen appears.
5. Log in. Observe: new empty conversation, typed prompt gone.

### Expected behaviour

- **The composer draft survives.** Persist it per conversation to local app state on every change and rehydrate after any auth or navigation event (also across app restarts). This alone removes the data loss regardless of the auth work below.
- **`returnTo` carries the current conversation URL**, not `/new`.
- **Elevated auth is not a logout.** When the session key is valid and only the scope expansion is stale, re-auth belongs in a separate popup window or modal that leaves the main view mounted; `from=logout` is wrong here.
- **Refresh proactively.** Refresh the OAuth token before it expires rather than discovering `invalid_grant` mid-session, and back off background scope expansions while the user is actively typing.
- **Fail soft.** If a draft is ever about to be dropped, surface a "restore draft" banner instead of discarding it silently.

### Impact

Direct loss of user work, caused entirely by a background auth operation the user did not initiate and cannot see coming. Long, carefully composed prompts are the highest-value input the product receives. The practical user response is to draft in an external editor, which defeats the purpose of the app.

### Environment

- Claude desktop app **1.34493.1** (`.deb`, `dpkg` package `claude-desktop` 1.34493.1, amd64)
- Electron 42.9.2, Node 24.18.1, Chromium renderer
- Ubuntu 26.04 LTS (Resolute Raccoon), kernel 7.0.0-30-generic, x86_64
- Occurred 2026-08-22 15:42 CEST; second forced login of the day
- Failing request id: `req_011CeHtEBsrjPEPfaS42Hg8R`

Guida per i contributori

Nessuna guida per i contributori indicizzata per questo repository

Direzione di ricerca

No source files or tests are named. Start by tracing the desktop app's background OAuth scope-expansion flow around /authorize and how it constructs login returnTo, using the supplied main.log sequence; then inspect how the composer draft behaves across top-level navigation. Done means re-login preserves the current conversation and draft without treating valid-session scope expansion as logout.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
electron, node.js
Ambito
authentication, desktop
Tipo di issue
Bug
Difficoltà
5/5
Tempo stimato
Più di una settimana
Stato di attività
Attiva
Chiarezza
Abbastanza chiara
Idoneità per principianti
30/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.