{"record":{"id":"13411b5a19e71dc0","repo":"vercel/ai","slug":"continuation-contains-a-non-json-serializable-valu","errorCode":null,"errorMessage":"Continuation contains a non-JSON-serializable value.","messagePattern":"Continuation contains a non-JSON-serializable value\\.","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/code-mode/src/continuation-capability.ts","lineNumber":218,"sourceCode":"  }\n  if (typeof value === 'string') {\n    return JSON.stringify(value);\n  }\n  if (typeof value === 'number' || typeof value === 'boolean') {\n    return JSON.stringify(value);\n  }\n  if (Array.isArray(value)) {\n    return `[${value.map(canonicalJson).join(',')}]`;\n  }\n  if (typeof value === 'object') {\n    const entries = Object.entries(value as Record<string, unknown>)\n      .filter(([, item]) => item !== undefined)\n      .sort(([left], [right]) => left.localeCompare(right));\n    return `{${entries\n      .map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`)\n      .join(',')}}`;\n  }\n  throw new TypeError('Continuation contains a non-JSON-serializable value.');\n}\n","sourceCodeStart":200,"sourceCodeEnd":220,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/code-mode/src/continuation-capability.ts#L200-L220","documentation":"canonicalJson builds a deterministic, key-sorted JSON serialization of the continuation so the HMAC signature is stable. It only supports null, string, number, boolean, arrays, and plain objects (undefined object values are skipped). Any other value — functions, symbols, bigint, Date objects, undefined at array/top level, Map/Set, class instances' non-enumerable bits — makes deterministic signing impossible and triggers this TypeError. It is thrown from canonicalJson, called by signContinuationPayload (and recursively by canonicalJson itself).","triggerScenarios":"Calling signCodeModeContinuation (or verifyCodeModeContinuation, which re-serializes via signContinuationPayload) with a continuation containing a value canonicalJson cannot represent: a Date, bigint, function, symbol, Map/Set, RegExp, or an array element of undefined.","commonSituations":"Embedding provider tool results that include Date objects into pendingInterruptions/resolutions; adding a bigint counter or function reference to the continuation payload; passing a class instance with inherited fields; forgetting to JSON-normalize values fetched from another API before signing.","solutions":["Convert non-JSON values to JSON-compatible ones before signing: Date -> toISOString(), bigint -> string/number, Map/Set -> plain objects/arrays.","Strip functions, symbols, and undefined array entries from the continuation payload.","JSON round-trip the payload (with a safe parse utility) before calling signCodeModeContinuation to normalize it.","Keep the continuation payload limited to plain serializable data (strings, numbers, booleans, nulls, arrays, plain objects).","Wrap signCodeModeContinuation calls in a check that deep-validates serializability when payload content comes from dynamic sources."],"exampleFix":"// before\nconst continuation = signCodeModeContinuation({\n  ...base,\n  pendingInterruptions: [{ toolCallId: 't1', requestedAt: new Date() }],\n});\n// after\nconst continuation = signCodeModeContinuation({\n  ...base,\n  pendingInterruptions: [{ toolCallId: 't1', requestedAt: new Date().toISOString() }],\n});","handlingStrategy":"validation","validationCode":"function isJsonSerializable(value: unknown, seen = new Set()): boolean {\n  if (value === null || ['string','number','boolean'].includes(typeof value)) return true;\n  if (typeof value === 'bigint' || typeof value === 'function' || typeof value === 'symbol') return false;\n  if (value instanceof Date || value instanceof Map || value instanceof Set || value instanceof RegExp) return false;\n  if (seen.has(value)) return true;\n  seen.add(value);\n  if (Array.isArray(value)) return value.every(v => v !== undefined && isJsonSerializable(v, seen));\n  if (typeof value === 'object') return Object.values(value).every(v => isJsonSerializable(v, seen));\n  return false;\n}\nif (!isJsonSerializable(unsignedContinuation)) {\n  throw new Error('Normalize the continuation payload to plain JSON before signing');\n}","typeGuard":"function isPlainJsonValue(value: unknown): boolean {\n  return value === null || ['string','number','boolean'].includes(typeof value) ||\n    (Array.isArray(value) && value.every(isPlainJsonValue)) ||\n    (typeof value === 'object' && value !== null && !(value instanceof Date) && Object.values(value).every(isPlainJsonValue));\n}","tryCatchPattern":"try {\n  const signed = signCodeModeContinuation(unsignedContinuation);\n} catch (e) {\n  if (e instanceof TypeError && /non-JSON-serializable/.test(e.message)) {\n    const normalized = JSON.parse(JSON.stringify(unsignedContinuation, (_k, v) =>\n      typeof v === 'bigint' ? v.toString() : v instanceof Date ? v.toISOString() : v));\n    const signed = signCodeModeContinuation(normalized);\n  } else throw e;\n}","preventionTips":["Keep continuation payloads limited to plain JSON types: string, number, boolean, null, array, plain object.","Convert Date objects with toISOString() and bigints with String() before signing.","Strip functions, symbols, and undefined array entries from payloads.","JSON round-trip dynamic provider data before embedding it in a continuation.","Add a pre-sign serializability assertion in your own wrapper around signCodeModeContinuation."],"tags":["code-mode","continuation","serialization","typeerror"],"backgroundTag":"json-serialization-failed","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}