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
This commit is contained in:
7418 committed 2026-02-24 17:50:49 +08:00
1 parent 979e1b1973
commit 61e093094e
1 file changed
+31 -1
+31 -1
View File
@@ -533,11 +533,41 @@ export function streamClaude(options: ClaudeStreamOptions): ReadableStream<strin
}
}
const conversation = query({
// Try to start the conversation. If resuming a previous session fails
// (e.g. stale/corrupt session file, CLI version mismatch), automatically
// fall back to starting a fresh conversation without resume.
let conversation = query({
prompt: finalPrompt,
options: queryOptions,
});
// Wrap the iterator so we can detect resume failures on the first message
if (sdkSessionId) {
try {
// Peek at the first message to verify resume works
const iter = conversation[Symbol.asyncIterator]();
const first = await iter.next();
// Re-wrap into an async iterable that yields the first message then the rest
conversation = (async function* () {
if (!first.done) yield first.value;
while (true) {
const next = await iter.next();
if (next.done) break;
yield next.value;
}
})() as ReturnType<typeof query>;
} catch (resumeError) {
console.warn('[claude-client] Resume failed, retrying without resume:', resumeError instanceof Error ? resumeError.message : resumeError);
// Remove resume and try again as a fresh conversation
delete queryOptions.resume;
conversation = query({
prompt: finalPrompt,
options: queryOptions,
});
}
}
registerConversation(sessionId, conversation);
let lastAssistantText = '';