fix(server): respawn Claude after an expired login - #9468
Closed
robertnisipeanu wants to merge 2 commits into
Closed
fix(server): respawn Claude after an expired login#9468robertnisipeanu wants to merge 2 commits into
robertnisipeanu wants to merge 2 commits into
Conversation
Claude Code loads its OAuth credentials once per process. After they expire, the long-lived CLI behind a thread keeps answering "Not logged in" even once the user has run /login elsewhere, until the idle reaper kills it. When the CLI reports an assistant message with error "authentication_failed", the adapter fails the turn with the CLI's message and stops the session, so the next turn spawns a fresh CLI with the current credentials. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — The production adapter changes authentication-failure handling and terminates long-lived Claude sessions so later requests can respawn with refreshed credentials. Although the change is narrowly scoped and tested, its credential and session-lifecycle implications require focused review. You can add or adjust custom eligibility rules. Learn more. |
…respawn # Conflicts: # apps/server/src/provider/Layers/ClaudeAdapter.ts
Member
aorwall
added a commit
to aorwall/t3code
that referenced
this pull request
Sep 8, 2026
Merges `pingdotgg/t3code` `8b2838e0e..a37c664` — 43 commits. `343` files landed against `343` changed in the upstream range; fork delta `723` files. Exact match, so nothing upstream changed was dropped. Details in [`docs/fork/upstream-merge-log.md`](../blob/merge/upstream-2026-09-08/docs/fork/upstream-merge-log.md). ## Two fork deltas this merge had to re-apply **Upstream split the server-update banner into two routes.** pingdotgg#10596 added `useAutoBalanceUpdateBanner` beside the single-machine condition the fork already gates. The conflict was on the first line only, so resolving it correctly still left the auto-balance route ungated — an auto-balanced project would have been offered `npx t3` against a backend that does not implement `server.updateServer`. `FEATURES.serverUpdateBanner` now carries two gates in `ChatView.tsx`. **A new settings page needs a gate even though it degrades politely.** pingdotgg#8103 added `/settings/snap-shot` for desktop window capture. Every control drives `window.desktopBridge`, and upstream renders an "unavailable" notice rather than hiding the page, so a hosted build listed a sidebar section and six searchable rows for a feature it can never run. Gated with `FEATURES.snapShots`. Two smaller fixes: `packages/moatless-api` still ran `tsgo --noEmit` after upstream replaced `@typescript/native-preview` with TypeScript 7.0.2, and `duplicate-adds.mjs` now skips `pnpm-lock.yaml` (it read `iconv-lite: 0.6.3` as taken twice; `d3-dsv` and `encoding` each declare it). ## Usable as-is - Stop-thread keybinding command (pingdotgg#4308). - Project import tolerates servers that predate the git-identity scan (pingdotgg#10547). - Proactive panels open when entering a thread (pingdotgg#10610); pull-request markdown links open in the panel (pingdotgg#10623); markdown images navigate as galleries (pingdotgg#10625); pull-request videos play inline (pingdotgg#10617). - Settings project scopes are searchable and scrollable (pingdotgg#10570); ref picker stays steady when opening (pingdotgg#9472); sidebar timer uses `tabular-nums` (pingdotgg#10592); popup triggers stay steady when pressed (pingdotgg#9468); settled PR colors restore on hover (pingdotgg#10023). - Composer Fast mode persists across new chats (pingdotgg#2981); inserted citations are removed on cancel (pingdotgg#10518). - TypeScript 7.0.2 (pingdotgg#10663) and the knip desktop-export rules (pingdotgg#10269). ## Unsupported in Moatless / needs implementation - **Cross-platform window capture** (pingdotgg#8103) — `apps/desktop/src/snapShot/**`, `apps/web/src/components/settings/SnapShotSettings.tsx`, `apps/web/src/lib/desktopSnapShot.ts`. Needs an Electron `window.desktopBridge`; a browser tab has none. Gated behind `FEATURES.snapShots` in this PR. - **Auto-balance server update** (pingdotgg#10596) — `apps/web/src/components/chat/useAutoBalanceUpdateBanner.tsx`. Needs `server.updateServer`, which Moatless does not dispatch. Gated behind `FEATURES.serverUpdateBanner` in this PR. - **Preview recording transfer** (pingdotgg#10572) — `apps/server/src/mcp/toolkits/preview/handlers.ts`, `apps/web/src/browser/browserRecordingUpload.ts`. Moves a finished preview recording into the agent environment over the desktop bridge. Adds four error types to `packages/contracts/src/previewAutomation.ts` and no new RPC method, so no union changed. Sits behind the `previewAutomation.connect` / `focusHost` / `respond` gap already in the register. - **Local media linked from remote threads** (pingdotgg#10619) and **browser editing shortcuts** (pingdotgg#10621) — Electron shell only. - **iOS Keychain access group** (pingdotgg#3665) and the mobile provider account badge (pingdotgg#9899) — the fork ships no mobile build against Moatless. ## Backend behavior to consider reproducing in Moatless - **Name the usage limit and its reset instead of relaying "out of credits"** (pingdotgg#10473, `apps/server/src/provider/**` Codex adapter). Moatless owns its provider runtime, so the clearer limit message has to be produced there. - **Report usage limits on retried turns** (pingdotgg#10549, Claude adapter). A retry currently loses the limit signal; same ownership. - **Disable executable capabilities in Claude metadata generation** (pingdotgg#4169, `apps/server/src/textGeneration/ClaudeTextGeneration.ts`). Title and metadata generation should not be able to run tools. Worth mirroring wherever Moatless generates thread titles. ## Verification `verify.mjs`: duplicate-adds, tripwires, resolution-check, unsupported-methods (0 ADD, 0 DROP, 2 KEEP), fmt, lint and typecheck all pass. Tests pass except `@t3tools/desktop`, which cannot compile `scripts/browser-secret-native.test.mjs` because the sandbox has no `libsecret-1` — 1283 tests pass, 0 fail, and the file is byte-identical to upstream. New entry in `docs/fork/gaps.md`. `t3` failed `GrokAdapter.test.ts` once under parallel load and passes 42/42 alone. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- Moatless task: https://moatless.soaplabstest.com/tasks/6d8ea486-2fcb-4c25-bd34-dcd15cc4a7ac
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #9607
Problem
Claude Code loads its OAuth credentials once per process. T3 Code keeps one long-lived CLI per thread, so after the credentials expire that thread keeps answering "Not logged in · Please run /login" even after the user logs in again from a terminal. New threads work because they spawn a fresh CLI. The stuck thread only recovers once the idle reaper kills its process, up to 30 minutes later, or after a manual Stop.
Fix
When the CLI reports an assistant message with
error: "authentication_failed", the Claude adapter:The stop runs on the stream fiber itself, so
stopSessionInternalskips interrupting that fiber and lets the stream end through its ownstoppedcheck. The first auth-failure message in a turn wins, so a repeated failure does not duplicate the runtime error or replace the CLI's message with the fallback.Testing
vp test run apps/server/src/provider/Layers/ClaudeAdapter.test.ts(81 passed, two new tests: auth failure with text, and repeated auth failure without text)Built with Claude Fable 5.1 in Claude Code.
🤖 Generated with Claude Code
Note
Medium Risk
Changes Claude session lifecycle and turn completion on auth errors; incorrect handling could drop sessions prematurely or leave stale CLI processes, but behavior is scoped to
authentication_failedand covered by new tests.Overview
Fixes threads stuck on “Not logged in” after the user re-authenticates by treating Claude CLI
authentication_failedassistant messages as a hard session failure and tearing down the long-lived process so the next message spawns a fresh CLI with current credentials.The Claude adapter records the first auth failure per turn in
authenticationError, fails the turn with the CLI’s text (or aruntime.errorplus fallback when there is no text), skips duplicating that message as a generic runtime error, and callsstopSessionInternalafterturn.completed.stopSessionInternalno longer interrupts the stream fiber when stop is invoked from that same fiber, avoiding a self-deadlock while the session exits.Tests cover auth failure with CLI text (failed turn,
session.exited, no session, one close) and repeated empty auth messages (single runtime error, first message wins).Reviewed by Cursor Bugbot for commit 5222edf. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Stop and respawn Claude session after expired login authentication failure
authentication-errorfield toClaudeSessionContextthat captures the first authentication-failure message from a Claude CLI assistant message; textless failures emit a fallback runtime error.makeClaudeAdapternow checks for a captured authentication error: if present, it completes the turn as failed with that message and stops the session, suppressing a duplicate runtime error. Normal failed results keep existing behavior.stopSessionInternalso that shutdown invoked from the stream fiber does not wait to interrupt that same fiber; distinct live stream fibers are still interrupted.makeClaudeAdapter.handleResultMessagesuppresses the duplicate when the assistant message already supplied the error text.Macroscope summarized 619ddee.