{"record":{"id":"b48fa9a4ae739b04","repo":"vercel/ai","slug":"code-mode-protocol-error-b48fa9","errorCode":"CODE_MODE_PROTOCOL_ERROR","errorMessage":"Code-mode interrupt outer tool call id does not match its continuation.","messagePattern":"Code-mode interrupt outer tool call id does not match its continuation\\.","errorType":"exception","errorClass":"CodeModeProtocolError","httpStatus":null,"severity":"error","filePath":"packages/code-mode/src/interrupt-continuation.ts","lineNumber":142,"sourceCode":"  value: unknown,\n  continuationSecurity: CodeModeContinuationSecurityOptions,\n): CodeModeInterrupt | undefined {\n  if (isCodeModeInterrupt(value, continuationSecurity)) {\n    return value;\n  }\n  if (\n    isRecord(value) &&\n    (value.type === 'json' || value.type === 'text') &&\n    'value' in value\n  ) {\n    return readInterruptValue(value.value, continuationSecurity);\n  }\n  return undefined;\n}\n\nfunction assertInterruptMatchesLedger(interrupt: CodeModeInterrupt): void {\n  if (interrupt.continuation.outerToolCallId !== interrupt.outerToolCallId) {\n    throw new CodeModeProtocolError(\n      'Code-mode interrupt outer tool call id does not match its continuation.',\n    );\n  }\n  const pending =\n    interrupt.continuation.pendingInterruptions[\n      interrupt.continuation.resolutions.length\n    ];\n  if (\n    pending === undefined ||\n    pending.interruptId !== interrupt.interruptId ||\n    pending.toolCallId !== interrupt.toolCallId ||\n    pending.toolName !== interrupt.toolName ||\n    !jsonEqual(pending.input, interrupt.input) ||\n    !jsonEqual(pending.payload, interrupt.payload)\n  ) {\n    throw new CodeModeProtocolError(\n      'Code-mode interrupt metadata does not match the signed continuation ledger.',\n      { interruptId: interrupt.interruptId },","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/code-mode/src/interrupt-continuation.ts#L124-L160","documentation":"assertInterruptMatchesLedger verifies that a CodeModeInterrupt object is internally consistent before it is accepted (in isCodeModeInterrupt) or continued (in continueCodeModeInterrupt). The interrupt carries an outerToolCallId at top level and inside its signed continuation ledger; when these differ the object is considered tampered, corrupt, or not a genuine code-mode interrupt, and a CodeModeProtocolError is thrown. In isCodeModeInterrupt the throw is caught and merely makes the guard return false.","triggerScenarios":"Calling continueCodeModeInterrupt with an interrupt whose outerToolCallId was manually edited, or an interrupt deserialized/transported incorrectly so the top-level id and the id embedded in the signed continuation token disagree.","commonSituations":"Persisting interrupts to a database and partially mutating them on replay; hand-constructing a CodeModeInterrupt instead of using the one returned by getCodeModeInterrupt/unwrapCodeModeResult; version mismatches between serialized interrupt formats.","solutions":["Use the interrupt object exactly as returned by unwrapCodeModeResult/getCodeModeInterrupt; never edit its fields.","Re-obtain a fresh interrupt by re-running the code-mode call if the stored one is suspect.","Verify your persistence layer serializes/deserializes the whole interrupt object losslessly (no partial updates)."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// Before resuming:\nconst valid = isCodeModeInterrupt(interrupt, continuationSecurity);\nif (!valid) throw new Error('interrupt is not a genuine code-mode interrupt');","typeGuard":"function isConsistentInterrupt(i: CodeModeInterrupt): boolean {\n  return i.continuation.outerToolCallId === i.outerToolCallId;\n}","tryCatchPattern":"try {\n  await continueCodeModeInterrupt({ interrupt, resolution, tools });\n} catch (error) {\n  if (CodeModeProtocolError.isInstance(error)) {\n    // discard tampered/corrupt interrupt, re-run the program\n  } else throw error;\n}","preventionTips":["Treat CodeModeInterrupt objects as opaque; never mutate any field.","Persist interrupts with lossless JSON serialization only.","Always re-validate with isCodeModeInterrupt after loading from storage."],"tags":["code-mode","protocol-error","continuation","integrity-check"],"backgroundTag":"continuation-ledger-mismatch","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}