{"record":{"id":"61cdbecbc17bf494","repo":"paperclipai/paperclip","slug":"the-bridge-host-reached-its-reserved-process-body-61cdbe","errorCode":null,"errorMessage":"The bridge host reached its reserved process body byte ceiling. Retry later.","messagePattern":"The bridge host reached its reserved process body byte ceiling\\. Retry later\\.","errorType":"exception","errorClass":"BridgeProcessCapacityError","httpStatus":null,"severity":"error","filePath":"packages/adapter-utils/src/sandbox-callback-bridge.ts","lineNumber":2256,"sourceCode":"// once, after the body is no longer needed, so its reserved bytes return to\n// the ledger on completion, on an error the caller raises later, on a client\n// abort, and on a timeout — every path funnels through the caller's own\n// finally block. A read that fails here (the size limit, or a denied\n// process reservation) releases its own partial reservation immediately, so\n// no caller-side release call is needed for that path.\nasync function readBodyBytes(req) {\n  const chunks = [];\n  let totalBytes = 0;\n  let reservedBytes = 0;\n  try {\n    for await (const chunk of req) {\n      const nextChunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n      totalBytes += nextChunk.byteLength;\n      if (totalBytes > maxBodyBytes) {\n        throw new Error(\"Bridge request body exceeded the configured size limit.\");\n      }\n      if (!processBodyLedger.reserve(nextChunk.byteLength)) {\n        throw new BridgeProcessCapacityError();\n      }\n      reservedBytes += nextChunk.byteLength;\n      chunks.push(nextChunk);\n    }\n    if (!processBodyLedger.reserve(totalBytes)) {\n      throw new BridgeProcessCapacityError();\n    }\n    reservedBytes += totalBytes;\n    const body = Buffer.concat(chunks);\n    return { body, release: () => processBodyLedger.release(reservedBytes) };\n  } catch (error) {\n    processBodyLedger.release(reservedBytes);\n    throw error;\n  }\n}\n\nasync function readBody(req) {\n  const { body, release } = await readBodyBytes(req);","sourceCodeStart":2238,"sourceCodeEnd":2274,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/adapter-utils/src/sandbox-callback-bridge.ts#L2238-L2274","documentation":"In the sandbox callback bridge, request body chunks are accumulated with a hard size check and incremental reservation on the shared processBodyLedger. This error is thrown when a chunk cannot be reserved because the host-wide reserved process body byte pool is exhausted (distinct from the hard 'exceeded the configured size limit' error for single oversized bodies).","triggerScenarios":"While reading a callback request body, processBodyLedger.reserve(nextChunk.byteLength) returns false for some chunk — total pool capacity is consumed by other concurrent bridge requests even though this body is under maxBodyBytes.","commonSituations":"Many sandbox callbacks arriving concurrently at one bridge host; a prior request leaked its reservation (release never ran); the pool ceiling configured below normal concurrency demands; a large in-flight body hogging capacity.","solutions":["Retry the callback request after a short delay; capacity returns when in-flight bodies complete.","Ensure release() (which returns reservedBytes to the ledger) executes on all error paths for every request.","Serialize or rate-limit sandbox callback requests to fit within the ledger ceiling.","Increase the process body byte ceiling in the sandbox callback bridge configuration."],"exampleFix":"// before\nif (!processBodyLedger.reserve(nextChunk.byteLength)) {\n  throw new BridgeProcessCapacityError();\n}\n// after\nif (!processBodyLedger.reserve(nextChunk.byteLength)) {\n  res.statusCode = 503; res.setHeader('retry-after', '2'); res.end();\n  throw new BridgeProcessCapacityError();\n}","handlingStrategy":"retry","validationCode":"const cl = Number(req.headers[\"content-length\"] ?? 0);\nif (cl > maxBodyBytes) { res.statusCode = 413; res.end(); return; }\nif (!processBodyLedger.canReserve(cl)) { res.statusCode = 503; res.setHeader(\"retry-after\", \"2\"); res.end(); return; }","typeGuard":"null","tryCatchPattern":"try {\n  const { body, release } = await reserveCallbackBody(req, ledger);\n  try { handle(body); } finally { release(); }\n} catch (err) {\n  if (err instanceof BridgeProcessCapacityError) respond503RetryLater(res);\n  else throw err;\n}","preventionTips":["Release reserved bytes in a finally block for every callback request.","Set client body size limits (413) before reading streams.","Throttle sandbox callback concurrency to fit the ledger.","Watch for pools that never drain — a sign of leaked reservations."],"tags":["capacity","sandbox","bridge","concurrency"],"backgroundTag":"resource-limit-exceeded","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}