paperclipai/paperclip · error

Bridge envelope too large

Error message

Bridge envelope too large

What it means

The bridge gateway's request poller reads each `.json` envelope and, before JSON-parsing it, checks Buffer.byteLength(raw) against maxEnvelopeBytes. If the raw envelope string is larger than the configured maximum it throws "Bridge envelope too large"; the enclosing catch converts this into a 400 "Invalid bridge request payload." response file for the request id. This is the final in-process size gate after the earlier stat/read checks, protecting the host from oversized payloads before parse and body decode.

Solutions

  1. Increase maxBodyBytes (and thereby maxEnvelopeBytes) in the bridge configuration to fit legitimate workloads.
  2. Have producers enforce the limit client-side with encodeSandboxBridgeBody(body, maxBodyBytes) before writing the request, so oversized requests fail fast with a clear message instead of a generic 400.
  3. Check the finalize response file for the request id — the gateway answers 400 "Invalid bridge request payload."; correlate the rejected request with what the agent tried to send.
  4. Use a queue client that supports fileSize/read limits so oversized envelopes are rejected earlier with the more specific 413 response.
  5. Split or compress large payloads instead of sending them as a single envelope body.

Example fix

// before: producer writes whatever it has, gateway rejects with a vague 400
await client.writeTextFile(requestPath, JSON.stringify({ body: bigPayload }));
// after: fail fast with the codec's own limit
const envelope = encodeSandboxBridgeBody(bigPayload, maxBodyBytes); // throws early if too large
await client.writeTextFile(requestPath, JSON.stringify(envelope));
Defensive patterns

Strategy: try-catch

Validate before calling

const raw = JSON.stringify(envelope);
const maxEnvelopeBytes = 6 * maxBodyBytes + 64 * 1024;
if (Buffer.byteLength(raw) > maxEnvelopeBytes) throw new Error(`request envelope ${Buffer.byteLength(raw)}B exceeds ${maxEnvelopeBytes}B`);

Try / catch

try {
  if (Buffer.byteLength(raw) > maxEnvelopeBytes) throw new Error("Bridge envelope too large");
  request = JSON.parse(raw);
  decodeSandboxBridgeBody(request, maxBodyBytes);
} catch {
  // gateway behaviour: reply 400 so the caller sees a definite failure
  await finalize({ id: requestId, status: 400, body: JSON.stringify({ error: "Invalid bridge request payload." }) });
}

Prevention

When it happens

Trigger: A request envelope whose serialized JSON string exceeds maxEnvelopeBytes (6 * maxBodyBytes + 64KiB) reaches the poller — typically because the queue client's readTextFile did not enforce a byte limit (maxBytes undefined, or a client implementation lacking fileSize/read limits), so the raw string arrives whole and only this check catches it.

Common situations: Sandbox jobs posting very large bodies (big file uploads, huge diffs) through the callback bridge; misconfigured maxBodyBytes far below the payloads agents legitimately send; using a custom/older queue client without built-in read limits, so enforcement falls solely to this gateway-side check.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18). Data as JSON: /api/errors/e23ca55274f02200. Report an issue: GitHub.

Appendix: source

Thrown at packages/adapter-utils/src/sandbox-callback-bridge.ts:1072

      let raw: string;
      try {
        raw = await input.client.readTextFile(requestPath, readLimit);
      } catch (error) {
        // The gateway deletes a request file when its caller stops waiting
        // (client-side timeout cleanup). A read that fails because the file is
        // gone is that benign race, not a channel fault: confirm the file
        // vanished and skip quietly instead of escalating into a recovery
        // pass. A file that is still listed rethrows, so a real read fault
        // keeps its existing handling.
        const remaining = await input.client.listJsonFiles(directories.requestsDir).catch(() => null);
        if (remaining !== null && !remaining.includes(fileName)) {
          return;
        }
        throw error;
      }
      let request: SandboxCallbackBridgeRequest;
      try {
        if (Buffer.byteLength(raw) > maxEnvelopeBytes) throw new Error("Bridge envelope too large");
        request = JSON.parse(raw) as SandboxCallbackBridgeRequest;
        decodeSandboxBridgeBody(request, maxBodyBytes);
      } catch {
        const requestId = fileName.replace(/\.json$/i, "") || randomUUID();
        await finalize({
          id: requestId,
          status: 400,
          headers: { "content-type": "application/json" },
          body: JSON.stringify({ error: "Invalid bridge request payload." }),
          completedAt: new Date().toISOString(),
        });
        return;
      }

      // Keep only the actual request allocation reserved while forwarding;
      // an abandoned small request must not retain a maximum-sized reservation.
      envelopeReadReservation.release();
      if (!reservation.reserve(4 * Buffer.byteLength(raw) + 2 * Buffer.byteLength(request.body))) {

View on GitHub (pinned to 3f1d897a7c)