{"record":{"id":"4bc31e1e6f0a4a53","repo":"paperclipai/paperclip","slug":"invalid-bridge-base64-body","errorCode":null,"errorMessage":"Invalid bridge base64 body.","messagePattern":"Invalid bridge base64 body\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/adapter-utils/src/sandbox-callback-bridge-body.ts","lineNumber":29,"sourceCode":"\nexport function encodeSandboxBridgeBody(body: string | Buffer, maxBodyBytes: number): SandboxCallbackBridgeBody {\n  if (Buffer.byteLength(body) > maxBodyBytes) throw new Error(\"Bridge body exceeded the configured size limit.\");\n  return Buffer.isBuffer(body) ? { body: body.toString(\"base64\"), bodyEncoding: \"base64\" } : { body };\n}\n\n/** Self-contained so the same decoder can be embedded in the remote gateway. */\nexport function decodeSandboxBridgeBody(envelope: SandboxCallbackBridgeBody, maxBodyBytes: number): Buffer {\n  if (!envelope || typeof envelope.body !== \"string\") throw new Error(\"Invalid bridge body.\");\n  if (envelope.bodyEncoding === undefined || envelope.bodyEncoding === \"utf8\") {\n    if (Buffer.byteLength(envelope.body, \"utf8\") > maxBodyBytes) throw new Error(\"Bridge body exceeded the configured size limit.\");\n    return Buffer.from(envelope.body, \"utf8\");\n  }\n  if (envelope.bodyEncoding !== \"base64\") throw new Error(\"Unsupported bridge body encoding.\");\n  const value = envelope.body;\n  if (value.length > 4 * Math.ceil(maxBodyBytes / 3)) throw new Error(\"Bridge body exceeded the configured size limit.\");\n  // Buffer.from is permissive; reject malformed input before allocating bytes.\n  if (value.length % 4 !== 0 || /[^A-Za-z0-9+/=]/.test(value) || !/^[A-Za-z0-9+/]*={0,2}$/.test(value)) {\n    throw new Error(\"Invalid bridge base64 body.\");\n  }\n  const bytes = Buffer.from(value, \"base64\");\n  if (bytes.length > maxBodyBytes) throw new Error(\"Bridge body exceeded the configured size limit.\");\n  if (bytes.toString(\"base64\") !== value) throw new Error(\"Invalid bridge base64 body.\");\n  return bytes;\n}\n\nexport function sandboxBridgeBodyCodecSource(): string {\n  return [sandboxBridgeEnvelopeLimit, encodeSandboxBridgeBody, decodeSandboxBridgeBody]\n    .map(fn => `const ${fn.name} = ${fn.toString()};`).join(\"\\n\");\n}\n","sourceCodeStart":11,"sourceCodeEnd":41,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/packages/adapter-utils/src/sandbox-callback-bridge-body.ts#L11-L41","documentation":"The bridge decoder does not trust Buffer.from(value, 'base64') because it is permissive and silently skips invalid characters. Before decoding, it validates strict base64 shape (length divisible by 4, only [A-Za-z0-9+/=], padding only as a final = or ==); after decoding, it round-trips (bytes.toString('base64') === value) to catch any residual ambiguity such as misplaced padding or non-canonical encodings. Either check failing throws 'Invalid bridge base64 body.'","triggerScenarios":"bodyEncoding is 'base64' and the body string: has length not divisible by 4; contains characters outside the base64 alphabet; has padding characters not exactly at the end (0, 1, or 2 trailing '=' allowed); or decodes to bytes whose re-encoding does not reproduce the original string (non-canonical encoding, misplaced '=').","commonSituations":"A producer truncates a base64 string mid-encoding; URL-safe base64 ('-' and '_') sent instead of standard base64; padding stripped by a JSON layer or manual string manipulation; whitespace/newlines embedded in the base64 body; double-encoding or corrupting the payload during transport.","solutions":["Re-encode the body on the producer with Buffer.from(bytes).toString('base64') so it is canonical padded standard base64.","Strip whitespace/newlines and convert URL-safe characters ('-' -> '+', '_' -> '/') before sending, or switch the producer to standard base64.","Check for truncation: length must be a multiple of 4 with at most two trailing '=' characters.","Diff the round-trip: compare Buffer.from(value, 'base64').toString('base64') with the original string locally to pinpoint the corruption."],"exampleFix":"// before\nconst body = raw.toString(\"base64url\").replace(/=+$/, \"\"); // non-canonical, unpadded\n// after\nconst body = raw.toString(\"base64\"); // canonical padded standard base64\nconst envelope = { body, bodyEncoding: \"base64\" };","handlingStrategy":"validation","validationCode":"function isCanonicalBase64(value) {\n  return typeof value === \"string\" && value.length % 4 === 0 &&\n    /^[A-Za-z0-9+/]*={0,2}$/.test(value) &&\n    Buffer.from(value, \"base64\").toString(\"base64\") === value;\n}\nif (envelope.bodyEncoding === \"base64\" && !isCanonicalBase64(envelope.body)) throw new Error(\"body must be canonical padded base64\");","typeGuard":"function isCanonicalBase64(value: unknown): value is string {\n  return typeof value === \"string\" && value.length % 4 === 0 &&\n    /^[A-Za-z0-9+/]*={0,2}$/.test(value) &&\n    Buffer.from(value, \"base64\").toString(\"base64\") === value;\n}","tryCatchPattern":"try {\n  const bytes = decodeSandboxBridgeBody(envelope, maxBodyBytes);\n} catch (err) {\n  if (err instanceof Error && err.message === \"Invalid bridge base64 body.\") {\n    throw new Error(\"Producer sent malformed base64; re-encode with Buffer.toString('base64')\");\n  } else throw err;\n}","preventionTips":["Always produce base64 with Buffer.toString('base64') — never hand-roll or strip padding.","Convert URL-safe base64 ('-','_') to standard ('+','/') and restore padding before sending.","Strip whitespace/newlines from base64 payloads before embedding in the envelope.","Unit-test the producer's encoding with the same round-trip check the decoder uses."],"tags":["nodejs","base64","validation","sandbox-bridge"],"backgroundTag":"invalid-argument-format","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}