{"record":{"id":"007b7f26d4082eba","repo":"paperclipai/paperclip","slug":"paperclip-runner-attachment-staging-size-denied","errorCode":"paperclip_runner_attachment_staging_size_denied","errorMessage":"paperclip_runner_attachment_staging_size_denied","messagePattern":"paperclip_runner_attachment_staging_size_denied","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/src/services/native-runtime/native-runner-file-handoff.ts","lineNumber":746,"sourceCode":"        // identifies the inode that is safe to clear.\n        if (safeToClear) {\n          await handle.truncate(0);\n          await handle.sync();\n        }\n      } finally {\n        await handle.close();\n      }\n    }\n  }\n}\n\n/** Stage already-authorized bytes using the same confined, scrubbed slots as wake files. */\nexport async function stageNativeRunnerAttachmentBytes(input: {\n  workspaceRoot: string;\n  body: Buffer;\n}): Promise<{ workspaceRelativePath: string; cleanup(): Promise<void> }> {\n  if (input.body.length > MAX_ATTACHMENT_BYTES) {\n    throw new Error(\"paperclip_runner_attachment_staging_size_denied\");\n  }\n  const workspaceRoot = await realpath(input.workspaceRoot);\n  const processDirectoryName = await currentStagingProcessDirectoryName();\n  let destination = \"\";\n  let release = () => undefined;\n  await withStagingRegistryLock(workspaceRoot, async () => {\n    const directory = await ensurePrivateStagingDirectory(\n      workspaceRoot,\n      processDirectoryName,\n    );\n    const active =\n      activeStagingPathsByWorkspace.get(workspaceRoot) ?? new Set<string>();\n    const reusable = await scrubNativeRunnerStagingResidue(\n      workspaceRoot,\n      active,\n      processDirectoryName,\n    );\n    destination = reusable[0] ?? path.join(directory, randomUUID());","sourceCodeStart":728,"sourceCodeEnd":764,"githubUrl":"https://github.com/paperclipai/paperclip/blob/3f1d897a7c018d76563a21c6e39c3c9b03933622/server/src/services/native-runtime/native-runner-file-handoff.ts#L728-L764","documentation":"Guard in stageNativeRunnerAttachmentBytes enforcing the maximum attachment payload size (MAX_ATTACHMENT_BYTES) before any staging work begins. Oversized bodies are rejected immediately with this coded error rather than being partially staged or silently truncated.","triggerScenarios":"Calling stageNativeRunnerAttachmentBytes with input.body.length > MAX_ATTACHMENT_BYTES, e.g. attempting to stage a multi-hundred-MB attachment into the runner workspace.","commonSituations":"Runner tries to hand off a huge generated artifact as an 'attachment'; caller mistakenly passes the whole file instead of a reference; configured attachment cap lowered while clients still send old large payloads.","solutions":["Reduce the body size before staging (compress, chunk, or upload via register_deliverable/attachments API instead)","Check MAX_ATTACHMENT_BYTES and stage a smaller file that fits the limit","For large deliverables, register a work product or published URL rather than staging raw bytes"],"exampleFix":"// before\nawait stageNativeRunnerAttachmentBytes({ workspaceRoot, body: hugeBuffer })\n// after\nif (hugeBuffer.length > MAX_ATTACHMENT_BYTES) throw new Error(\"split or upload via attachments API\");\nawait stageNativeRunnerAttachmentBytes({ workspaceRoot, body: withinLimitBuffer })","handlingStrategy":"validation","validationCode":"if (Buffer.byteLength(body) > MAX_ATTACHMENT_BYTES) {\n  throw new Error(`attachment body ${body.length} exceeds MAX_ATTACHMENT_BYTES (${MAX_ATTACHMENT_BYTES}); compress or upload via attachments API`);\n}","typeGuard":"const withinAttachmentLimit = (buf, max = MAX_ATTACHMENT_BYTES) => Buffer.isBuffer(buf) && buf.length <= max;","tryCatchPattern":"try { await stageNativeRunnerAttachmentBytes({ workspaceRoot, body }); } catch (e) { if (e.message === \"paperclip_runner_attachment_staging_size_denied\") { /* compress/chunk or switch to register_deliverable/published URL */ } else throw e; }","preventionTips":["Check payload size before calling staging, not after","Prefer publishing large artifacts (URL/work product) over staging raw bytes","Track MAX_ATTACHMENT_BYTES changes when upgrading the server"],"tags":["payload","limits","attachments"],"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-23T08:17:48.524Z"}