{"record":{"id":"a69d84272621ed73","repo":"paperclipai/paperclip","slug":"bridge-envelope-exceeded-the-configured-size-limit","errorCode":null,"errorMessage":"Bridge envelope exceeded the configured size limit.","messagePattern":"Bridge envelope exceeded the configured size limit\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/adapter-utils/src/sandbox-callback-bridge.ts","lineNumber":533,"sourceCode":"    makeDirs: async (remotePaths) => {\n      for (const remotePath of remotePaths) {\n        await fs.mkdir(remotePath, { recursive: true });\n      }\n    },\n    listJsonFiles: async (remotePath) => {\n      const entries = await fs.readdir(remotePath, { withFileTypes: true }).catch(() => []);\n      return entries\n        .filter((entry) => entry.isFile() && entry.name.endsWith(\".json\"))\n        .map((entry) => entry.name)\n        .sort((left, right) => left.localeCompare(right));\n    },\n    fileSize: async (remotePath) => (await fs.stat(remotePath)).size,\n    readTextFile: async (remotePath, maxBytes) => {\n      if (maxBytes === undefined) return fs.readFile(remotePath, \"utf8\");\n      const file = await fs.open(remotePath, \"r\");\n      try {\n        const stat = await file.stat();\n        if (stat.size > maxBytes) throw new Error(\"Bridge envelope exceeded the configured size limit.\");\n        const bytes = Buffer.alloc(Math.min(stat.size, maxBytes) + 1);\n        let length = 0;\n        while (length < bytes.length) {\n          const read = await file.read(bytes, length, bytes.length - length, length);\n          if (!read.bytesRead) break;\n          length += read.bytesRead;\n        }\n        if (length > stat.size) throw new Error(\"Bridge envelope changed while reading.\");\n        return bytes.subarray(0, length).toString(\"utf8\");\n      } finally { await file.close(); }\n    },\n    writeTextFile: async (remotePath, body) => {\n      await fs.mkdir(path.posix.dirname(remotePath), { recursive: true });\n      // Write to a temporary path that does NOT end in `.json`, then rename it\n      // onto the final `.json` path. A direct `writeFile` truncates the final\n      // path first, so a `.json`-only reader (the stdin poller) can see an\n      // empty or partial file. The atomic rename never exposes partial content.\n      const tempPath = `${remotePath}.paperclip-upload.decoded`;","sourceCodeStart":515,"sourceCodeEnd":551,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/packages/adapter-utils/src/sandbox-callback-bridge.ts#L515-L551","documentation":"The file-system sandbox callback bridge queue client's readTextFile(path, maxBytes) stats the remote envelope file first and throws \"Bridge envelope exceeded the configured size limit.\" when the file on disk is larger than the caller-provided maxBytes budget. This bounds memory allocation so a runaway or oversized request/response JSON file can never be read whole into the host process.","triggerScenarios":"Calling readTextFile with a maxBytes argument while the `.json` envelope file at remotePath has a stat.size greater than maxBytes — e.g. the gateway reads request files with a read limit derived from maxEnvelopeBytes (6 * maxBodyBytes + 64KiB) and a producer wrote an envelope bigger than that budget.","commonSituations":"A sandbox job POSTs a body larger than the configured maxBodyBytes so the encoded envelope exceeds the envelope limit; maxBodyBytes/maxEnvelopeBytes were lowered in config while old, larger envelopes still sit in the requests directory; a stuck/duplicate writer grew the file beyond the limit before the reader picked it up.","solutions":["Increase the bridge's maxBodyBytes / maxEnvelopeBytes configuration to cover legitimate payloads.","Delete or drain oversized stale envelope files from the requests directory so the poller stops tripping on them.","On the producer side, check body size against encodeSandboxBridgeBody's limit before writing the request file, and split or compress the payload.","Check for writers appending to the same request path without atomic rename; the bridge writes via temp-file + rename to avoid this.","If the file is smaller than intended, re-stat — a concurrent writer may have been mid-write; retry after the writer completes."],"exampleFix":"// before: reading with a tight limit trips on a 2MB envelope\nconst raw = await client.readTextFile(requestPath, 512 * 1024);\n// after: size the budget from the same formula the gateway uses\nconst maxEnvelopeBytes = 6 * maxBodyBytes + 64 * 1024;\nconst raw = await client.readTextFile(requestPath, Math.min(fileSize, maxEnvelopeBytes));","handlingStrategy":"validation","validationCode":"const maxEnvelopeBytes = 6 * maxBodyBytes + 64 * 1024;\nconst size = await client.fileSize(requestPath);\nif (size > maxEnvelopeBytes) throw new Error(`Envelope ${size} bytes exceeds limit ${maxEnvelopeBytes}`);","typeGuard":null,"tryCatchPattern":"try {\n  const raw = await client.readTextFile(requestPath, readLimit);\n} catch (error) {\n  if ((error as Error).message.includes(\"exceeded the configured size limit\")) {\n    await finalize({ id, status: 413, body: JSON.stringify({ error: \"envelope too large\" }) });\n    return; // do not retry with the same limit\n  }\n  throw error;\n}","preventionTips":["Size maxBodyBytes from the largest legitimate payload your agents send, plus headroom.","Call client.fileSize (when available) before reading so oversized files are skipped, not read.","Enforce the body limit on the producer with encodeSandboxBridgeBody before writing requests.","Clean up stale/oversized envelopes from the requests directory regularly.","Remember the envelope limit is ~6x the body limit plus 64KiB of metadata overhead."],"tags":["file-size","ipc","sandbox","limit"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"3f1d897a7c018d76563a21c6e39c3c9b03933622","analyzedAt":"2026-09-18T08:03:59.046Z","contentChangedAt":"2026-09-18T08:03:59.046Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}