fix: auto-recover from Gemini session corruption
Detect the Gemini API error 'function call turn comes immediately after a user turn or after a function response turn' which indicates corrupted session history. When detected: - Delete the corrupted transcript file - Remove the session entry from the store - Return a user-friendly message asking them to retry This prevents the error loop where every subsequent message fails with the same error until manual intervention. Fixes #296
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
|
import fs from "node:fs";
|
||||||
import { lookupContextTokens } from "../../agents/context.js";
|
import { lookupContextTokens } from "../../agents/context.js";
|
||||||
import { DEFAULT_CONTEXT_TOKENS } from "../../agents/defaults.js";
|
import { DEFAULT_CONTEXT_TOKENS } from "../../agents/defaults.js";
|
||||||
import { runWithModelFallback } from "../../agents/model-fallback.js";
|
import { runWithModelFallback } from "../../agents/model-fallback.js";
|
||||||
@@ -8,6 +9,7 @@ import {
|
|||||||
} from "../../agents/pi-embedded.js";
|
} from "../../agents/pi-embedded.js";
|
||||||
import {
|
import {
|
||||||
loadSessionStore,
|
loadSessionStore,
|
||||||
|
resolveSessionTranscriptPath,
|
||||||
type SessionEntry,
|
type SessionEntry,
|
||||||
saveSessionStore,
|
saveSessionStore,
|
||||||
} from "../../config/sessions.js";
|
} from "../../config/sessions.js";
|
||||||
@@ -346,6 +348,37 @@ export async function runReplyAgent(params: {
|
|||||||
const message = err instanceof Error ? err.message : String(err);
|
const message = err instanceof Error ? err.message : String(err);
|
||||||
const isContextOverflow =
|
const isContextOverflow =
|
||||||
/context.*overflow|too large|context window/i.test(message);
|
/context.*overflow|too large|context window/i.test(message);
|
||||||
|
const isSessionCorruption =
|
||||||
|
/function call turn comes immediately after|INVALID_ARGUMENT.*function/i.test(
|
||||||
|
message,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Auto-recover from Gemini session corruption by resetting the session
|
||||||
|
if (isSessionCorruption && sessionKey && sessionStore && storePath) {
|
||||||
|
const corruptedSessionId = sessionEntry?.sessionId;
|
||||||
|
defaultRuntime.error(
|
||||||
|
`Session history corrupted (Gemini function call ordering). Resetting session: ${sessionKey}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Delete transcript file if it exists
|
||||||
|
if (corruptedSessionId) {
|
||||||
|
const transcriptPath = resolveSessionTranscriptPath(corruptedSessionId);
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(transcriptPath);
|
||||||
|
} catch {
|
||||||
|
// Ignore if file doesn't exist
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove session entry from store
|
||||||
|
delete sessionStore[sessionKey];
|
||||||
|
await saveSessionStore(storePath, sessionStore);
|
||||||
|
|
||||||
|
return finalizeWithFollowup({
|
||||||
|
text: "⚠️ Session history was corrupted. I've reset the conversation - please try again!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
defaultRuntime.error(`Embedded agent failed before reply: ${message}`);
|
defaultRuntime.error(`Embedded agent failed before reply: ${message}`);
|
||||||
return finalizeWithFollowup({
|
return finalizeWithFollowup({
|
||||||
text: isContextOverflow
|
text: isContextOverflow
|
||||||
|
|||||||
Reference in New Issue
Block a user