diff --git a/src/hooks/session-recovery/detect-error-type.test.ts b/src/hooks/session-recovery/detect-error-type.test.ts index de5765c05..f3fdccfa8 100644 --- a/src/hooks/session-recovery/detect-error-type.test.ts +++ b/src/hooks/session-recovery/detect-error-type.test.ts @@ -36,6 +36,31 @@ describe("detectErrorType", () => { expect(result).toBe("thinking_disabled_violation") }) + it("#given a Bedrock thinking block modified error #when detecting #then returns thinking_block_modified", () => { + //#given + const error = { + message: + "undefined: The model returned the following errors: messages.17.content.28: `thinking` or `redacted_thinking` blocks in the latest assistant message cannot be modified. These blocks must remain as they were in the original response.", + } + + //#when + const result = detectErrorType(error) + + //#then + expect(result).toBe("thinking_block_modified") + }) + + it("#given a simple thinking block modified error #when detecting #then returns thinking_block_modified", () => { + //#given + const error = { message: "thinking blocks cannot be modified" } + + //#when + const result = detectErrorType(error) + + //#then + expect(result).toBe("thinking_block_modified") + }) + it("#given an unrecognized error #when detecting #then returns null", () => { //#given const error = { message: "some random error" } diff --git a/src/hooks/session-recovery/detect-error-type.ts b/src/hooks/session-recovery/detect-error-type.ts index b5783dae4..ef849a6c7 100644 --- a/src/hooks/session-recovery/detect-error-type.ts +++ b/src/hooks/session-recovery/detect-error-type.ts @@ -2,6 +2,7 @@ export type RecoveryErrorType = | "tool_result_missing" | "thinking_block_order" | "thinking_disabled_violation" + | "thinking_block_modified" | "assistant_prefill_unsupported" | "unavailable_tool" | null @@ -77,6 +78,11 @@ export function detectErrorType(error: unknown): RecoveryErrorType { return "thinking_block_order" } + // Thinking block signature corruption (Bedrock compaction) + if (message.includes("thinking") && message.includes("cannot be modified")) { + return "thinking_block_modified" + } + if (message.includes("thinking is disabled") && message.includes("cannot contain")) { return "thinking_disabled_violation" } diff --git a/src/hooks/session-recovery/hook.ts b/src/hooks/session-recovery/hook.ts index 833dcf5dd..056065b19 100644 --- a/src/hooks/session-recovery/hook.ts +++ b/src/hooks/session-recovery/hook.ts @@ -99,6 +99,7 @@ export function createSessionRecoveryHook(ctx: PluginInput, options?: SessionRec unavailable_tool: "Tool Recovery", thinking_block_order: "Thinking Block Recovery", thinking_disabled_violation: "Thinking Strip Recovery", + thinking_block_modified: "Thinking Block Recovery", "assistant_prefill_unsupported": "Prefill Unsupported", } const toastMessages: Record = { @@ -106,6 +107,7 @@ export function createSessionRecoveryHook(ctx: PluginInput, options?: SessionRec unavailable_tool: "Recovering from unavailable tool call...", thinking_block_order: "Fixing message structure...", thinking_disabled_violation: "Stripping thinking blocks...", + thinking_block_modified: "Stripping corrupted thinking blocks...", "assistant_prefill_unsupported": "Prefill not supported; continuing without recovery.", } @@ -140,6 +142,13 @@ export function createSessionRecoveryHook(ctx: PluginInput, options?: SessionRec const resumeConfig = extractResumeConfig(lastUser, sessionID) await resumeSession(ctx.client, resumeConfig) } + } else if (errorType === "thinking_block_modified") { + success = await recoverThinkingDisabledViolation(ctx.client, sessionID, failedMsg) + if (success && experimental?.auto_resume) { + const lastUser = findLastUserMessage(msgs ?? []) + const resumeConfig = extractResumeConfig(lastUser, sessionID) + await resumeSession(ctx.client, resumeConfig) + } } else if (errorType === "assistant_prefill_unsupported") { success = false }