Skip to content

Commit 9d3c519

Browse files
committed
feat(ai): detect expired Claude login and guide re-login
Expired/revoked OAuth logins only fail at request time — auth status still reports logged in, so the panel previously showed a bare "exited with error code" message. Classify auth failures in the query error path (isAuthError on the aiError event) and add strings/styles for the re-login notice, terminal button and /login hint. API-key providers keep the existing settings-dialog hint instead.
1 parent 153ceb4 commit 9d3c519

3 files changed

Lines changed: 33 additions & 1 deletion

File tree

src-node/claude-code-agent.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2132,9 +2132,21 @@ async function _runQuery(requestId, prompt, projectPath, model, signal, locale,
21322132
// transient (network, rate limit), and if the session really is broken
21332133
// the next attempt will surface a fresh error of its own.
21342134

2135+
// Expired/revoked OAuth logins only surface at request time — `claude
2136+
// auth status` still reports loggedIn because the credentials exist,
2137+
// but the CLI exits non-zero with a /login hint. Flag it so the
2138+
// panel can offer a re-login action instead of a raw exit-code
2139+
// error. Custom API-key providers are excluded — their fix is the
2140+
// settings-dialog hint appended above.
2141+
const usingApiKey = !!(envOverrides && envOverrides.ANTHROPIC_AUTH_TOKEN);
2142+
const isAuthError = !usingApiKey &&
2143+
/run \/login|invalid api key|not logged in|oauth token|revoke|authentication[_ ]?error/i
2144+
.test(detailedError);
2145+
21352146
nodeConnector.triggerPeer("aiError", {
21362147
requestId: requestId,
2137-
error: detailedError
2148+
error: detailedError,
2149+
isAuthError: isAuthError
21382150
});
21392151

21402152
// Always send aiComplete after aiError so the UI exits streaming state

src/nls/root/strings.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2648,6 +2648,9 @@ define({
26482648
"AI_CHAT_MODE_INFO_EDIT": "AI can edit files. Shell commands need approval (Click to switch)",
26492649
"AI_CHAT_MODE_INFO_FULL_AUTO": "AI can edit files and run commands without approval (Click to switch)",
26502650
"AI_CHAT_MODE_SWITCH_HINT": "[or Shift+Tab]",
2651+
"AI_CHAT_AUTH_ERROR_NOTICE": "Claude Code is signed out or your login has expired.",
2652+
"AI_CHAT_AUTH_ERROR_BTN": "Log in to Claude in Terminal",
2653+
"AI_CHAT_AUTH_ERROR_HINT": "Type /login in the terminal that opens, then send your message again.",
26512654
"AI_CHAT_MODEL_DEFAULT": "Default",
26522655
"AI_CHAT_MODEL_DEFAULT_DESC_CURRENT": "Currently {0}",
26532656
"AI_CHAT_MODEL_SELECT_TITLE": "Choose the AI model for this chat",

src/styles/Extn-AIChatPanel.less

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,23 @@
239239
}
240240
}
241241

242+
/* Auth-error action block: button with its /login hint stacked below —
243+
tells the user the one thing to do next at a glance. */
244+
.ai-auth-error-actions {
245+
flex-direction: column;
246+
align-items: center;
247+
gap: 0;
248+
}
249+
250+
.ai-auth-error-hint {
251+
margin-top: 6px;
252+
font-size: @ai-text-meta;
253+
color: @project-panel-text-2;
254+
text-align: center;
255+
white-space: normal;
256+
overflow-wrap: break-word;
257+
}
258+
242259
/* One-time inline notice after switching model mid-conversation.
243260
Sentence-length status prose: secondary tier (12px), reading line-height,
244261
left-aligned — meta 11px/centered is for one-line stats, not sentences. */

0 commit comments

Comments
 (0)