paperclipai/paperclip · error
Bridge envelope exceeded the configured size limit.
Error message
Bridge envelope exceeded the configured size limit.
What it means
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.
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.
Example fix
// before: reading with a tight limit trips on a 2MB envelope const raw = await client.readTextFile(requestPath, 512 * 1024); // after: size the budget from the same formula the gateway uses const maxEnvelopeBytes = 6 * maxBodyBytes + 64 * 1024; const raw = await client.readTextFile(requestPath, Math.min(fileSize, maxEnvelopeBytes));
Defensive patterns
Strategy: validation
Validate before calling
const maxEnvelopeBytes = 6 * maxBodyBytes + 64 * 1024;
const size = await client.fileSize(requestPath);
if (size > maxEnvelopeBytes) throw new Error(`Envelope ${size} bytes exceeds limit ${maxEnvelopeBytes}`); Try / catch
try {
const raw = await client.readTextFile(requestPath, readLimit);
} catch (error) {
if ((error as Error).message.includes("exceeded the configured size limit")) {
await finalize({ id, status: 413, body: JSON.stringify({ error: "envelope too large" }) });
return; // do not retry with the same limit
}
throw error;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "File too large" / "file size exceeds limit" errors: why libraries cap file sizes and how to fix them — this error's family across 46 libraries.
Related errors
- Bridge envelope too large
- Bridge response envelope changed while reading.
- Bridge response envelope exceeded the configured size limit.
- A sandbox command is required.
- ACPX module must be a bounded regular file
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/a69d84272621ed73.
Report an issue: GitHub.
Appendix: source
Thrown at packages/adapter-utils/src/sandbox-callback-bridge.ts:533
makeDirs: async (remotePaths) => {
for (const remotePath of remotePaths) {
await fs.mkdir(remotePath, { recursive: true });
}
},
listJsonFiles: async (remotePath) => {
const entries = await fs.readdir(remotePath, { withFileTypes: true }).catch(() => []);
return entries
.filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
.map((entry) => entry.name)
.sort((left, right) => left.localeCompare(right));
},
fileSize: async (remotePath) => (await fs.stat(remotePath)).size,
readTextFile: async (remotePath, maxBytes) => {
if (maxBytes === undefined) return fs.readFile(remotePath, "utf8");
const file = await fs.open(remotePath, "r");
try {
const stat = await file.stat();
if (stat.size > maxBytes) throw new Error("Bridge envelope exceeded the configured size limit.");
const bytes = Buffer.alloc(Math.min(stat.size, maxBytes) + 1);
let length = 0;
while (length < bytes.length) {
const read = await file.read(bytes, length, bytes.length - length, length);
if (!read.bytesRead) break;
length += read.bytesRead;
}
if (length > stat.size) throw new Error("Bridge envelope changed while reading.");
return bytes.subarray(0, length).toString("utf8");
} finally { await file.close(); }
},
writeTextFile: async (remotePath, body) => {
await fs.mkdir(path.posix.dirname(remotePath), { recursive: true });
// Write to a temporary path that does NOT end in `.json`, then rename it
// onto the final `.json` path. A direct `writeFile` truncates the final
// path first, so a `.json`-only reader (the stdin poller) can see an
// empty or partial file. The atomic rename never exposes partial content.
const tempPath = `${remotePath}.paperclip-upload.decoded`;View on GitHub (pinned to 3f1d897a7c)