Skip to content

Commit 61e0930

Browse files
author
7418
committed
fix: auto-fallback to fresh conversation when SDK session resume fails
When continuing an old chat session, CodePilot passes the stored sdk_session_id to the SDK with resume=true. If the local SDK session file is stale, corrupt, or incompatible (e.g. after a CLI version update), the SDK process exits with code 1 and the user sees a misleading "Invalid or missing API Key" error. This fix wraps the initial query() call with a try-catch that detects resume failures by peeking at the first async message. On failure, it automatically retries without the resume option, starting a fresh SDK session while preserving the chat history in CodePilot. - src/lib/claude-client.ts: added resume-failure detection and automatic fallback logic around the query() call
1 parent 979e1b1 commit 61e0930

File tree

1 file changed

+31
-1
lines changed

1 file changed

+31
-1
lines changed

src/lib/claude-client.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,11 +533,41 @@ export function streamClaude(options: ClaudeStreamOptions): ReadableStream<strin
533533
}
534534
}
535535

536-
const conversation = query({
536+
// Try to start the conversation. If resuming a previous session fails
537+
// (e.g. stale/corrupt session file, CLI version mismatch), automatically
538+
// fall back to starting a fresh conversation without resume.
539+
let conversation = query({
537540
prompt: finalPrompt,
538541
options: queryOptions,
539542
});
540543

544+
// Wrap the iterator so we can detect resume failures on the first message
545+
if (sdkSessionId) {
546+
try {
547+
// Peek at the first message to verify resume works
548+
const iter = conversation[Symbol.asyncIterator]();
549+
const first = await iter.next();
550+
551+
// Re-wrap into an async iterable that yields the first message then the rest
552+
conversation = (async function* () {
553+
if (!first.done) yield first.value;
554+
while (true) {
555+
const next = await iter.next();
556+
if (next.done) break;
557+
yield next.value;
558+
}
559+
})() as ReturnType<typeof query>;
560+
} catch (resumeError) {
561+
console.warn('[claude-client] Resume failed, retrying without resume:', resumeError instanceof Error ? resumeError.message : resumeError);
562+
// Remove resume and try again as a fresh conversation
563+
delete queryOptions.resume;
564+
conversation = query({
565+
prompt: finalPrompt,
566+
options: queryOptions,
567+
});
568+
}
569+
}
570+
541571
registerConversation(sessionId, conversation);
542572

543573
let lastAssistantText = '';

0 commit comments

Comments
 (0)