{"record":{"id":"df571c2d5aaf6873","repo":"paperclipai/paperclip","slug":"plugin-api-request-body-is-too-large","errorCode":null,"errorMessage":"Plugin API request body is too large","messagePattern":"Plugin API request body is too large","errorType":"http","errorClass":null,"httpStatus":413,"severity":"error","filePath":"server/src/routes/plugins.ts","lineNumber":1887,"sourceCode":"    }\n\n    try {\n      assertScopedApiAuth(req, match.route);\n      const companyId = await resolveScopedApiCompanyId(match.route, match.params, req);\n      if (!companyId) {\n        res.status(400).json({ error: \"Unable to resolve company for plugin API route\" });\n        return;\n      }\n      assertCompanyAccess(req, companyId);\n      await enforceScopedApiCheckout(req, match.route, match.params, companyId);\n      if (req.method !== \"GET\" && req.headers[\"content-type\"] && !req.is(\"application/json\")) {\n        res.status(415).json({ error: \"Plugin API routes accept JSON requests only\" });\n        return;\n      }\n      const requestBody = req.body ?? null;\n      const bodySize = Buffer.byteLength(JSON.stringify(requestBody));\n      if (bodySize > PLUGIN_API_BODY_LIMIT_BYTES) {\n        res.status(413).json({ error: \"Plugin API request body is too large\" });\n        return;\n      }\n\n      const actor = getActorInfo(req);\n      const input: PluginScopedApiRequest = {\n        routeKey: match.route.routeKey,\n        method: req.method,\n        path: requestPath,\n        params: match.params,\n        query: normalizeQuery(req.query),\n        body: requestBody,\n        actor: {\n          actorType: actor.actorType,\n          actorId: actor.actorId,\n          agentId: actor.agentId,\n          userId: actor.actorType === \"user\" ? actor.actorId : null,\n          runId: actor.runId,\n        },","sourceCodeStart":1869,"sourceCodeEnd":1905,"githubUrl":"https://github.com/paperclipai/paperclip/blob/a7e689b3c35347b529cb9f54c9b9a8575a3dcab6/server/src/routes/plugins.ts#L1869-L1905","documentation":"Returned as HTTP 413 when Buffer.byteLength(JSON.stringify(requestBody)) exceeds PLUGIN_API_BODY_LIMIT_BYTES, which is 1,000,000 bytes (~1 MB). The check runs on the parsed body after auth/company/checkout guards, so even structurally valid JSON is rejected purely for size before the request is forwarded to the plugin worker.","triggerScenarios":"POST to /api/plugins/:pluginId/api/* with a JSON payload over ~1 MB: large batch arrays, base64-encoded file contents embedded in JSON, or deeply populated bulk-import payloads.","commonSituations":"Bulk import/sync endpoints exposed as plugin API routes; clients inlining images or documents as base64; generated clients that echo entire collections in one request.","solutions":["Split the payload into multiple requests each comfortably under 1 MB and batch server-side.","Upload large blobs to object storage or a dedicated upload endpoint and pass a reference (URL/key) in the plugin API body.","Trim redundant or computed fields from the JSON before sending."],"exampleFix":"// before\nawait fetch(url, { method: \"POST\", headers, body: JSON.stringify({ items: allItems }) });\n\n// after\nfunction chunk<T>(arr: T[], n: number): T[][] {\n  const out: T[][] = [];\n  for (let i = 0; i < arr.length; i += n) out.push(arr.slice(i, i + n));\n  return out;\n}\nfor (const batch of chunk(allItems, 200)) {\n  await fetch(url, { method: \"POST\", headers, body: JSON.stringify({ items: batch }) });\n}","handlingStrategy":"validation","validationCode":"const PLUGIN_API_BODY_LIMIT_BYTES = 1_000_000;\nfunction assertBodyWithinLimit(body: unknown): void {\n  const size = Buffer.byteLength(JSON.stringify(body));\n  if (size > PLUGIN_API_BODY_LIMIT_BYTES) {\n    throw new Error(`Body is ${size} bytes; plugin API limit is ${PLUGIN_API_BODY_LIMIT_BYTES}`);\n  }\n}","typeGuard":"function fitsPluginApiLimit(body: unknown): boolean {\n  try { return Buffer.byteLength(JSON.stringify(body)) <= 1_000_000; }\n  catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["Design plugin API payloads as bounded pages/chunks, never unbounded collections.","Store large blobs externally and pass references instead of inlining base64.","Measure serialized size client-side before sending when payloads approach 1 MB."],"tags":["plugin","http-413","payload-limit","scoped-api"],"backgroundTag":"payload-too-large","analyzedSha":"a7e689b3c35347b529cb9f54c9b9a8575a3dcab6","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}