{"record":{"id":"ec5569d1f60e7461","repo":"paperclipai/paperclip","slug":"github-attachment-too-large","errorCode":"github_attachment_too_large","errorMessage":"github_attachment_too_large","messagePattern":"github_attachment_too_large","errorType":"error_code","errorClass":"GitHubAttachmentUnavailableError","httpStatus":null,"severity":"warning","filePath":"server/src/services/chat-github-attachments.ts","lineNumber":800,"sourceCode":"        (!/^\\d+$/.test(declared) || Number(declared) > MAX_ATTACHMENT_BYTES)\n      )\n        return await rejectResponse(\"github_attachment_too_large\");\n      const reader = response.body.getReader();\n      const chunks: Buffer[] = [];\n      let size = 0;\n      const cancel = () => {\n        void reader.cancel().catch(() => undefined);\n      };\n      signal.addEventListener(\"abort\", cancel, { once: true });\n      try {\n        for (;;) {\n          signal.throwIfAborted();\n          const next = await reader.read();\n          signal.throwIfAborted();\n          if (next.done) break;\n          size += next.value.byteLength;\n          if (size > MAX_ATTACHMENT_BYTES)\n            throw new GitHubAttachmentUnavailableError(\n              \"github_attachment_too_large\",\n            );\n          chunks.push(Buffer.from(next.value));\n        }\n      } finally {\n        signal.removeEventListener(\"abort\", cancel);\n        await reader.cancel().catch(() => undefined);\n      }\n      if (!size)\n        throw new GitHubAttachmentUnavailableError(\"github_attachment_empty\");\n      // Content-Length describes compressed bytes when Content-Encoding is present.\n      if (\n        declared &&\n        !response.headers.get(\"content-encoding\") &&\n        size !== Number(declared)\n      )\n        throw new GitHubAttachmentUnavailableError(\n          \"github_attachment_invalid_response\",","sourceCodeStart":782,"sourceCodeEnd":818,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/server/src/services/chat-github-attachments.ts#L782-L818","documentation":"During streamed download of a public GitHub attachment, the reader loop at chat-github-attachments.ts:799-802 accumulates bytes and throws GitHubAttachmentUnavailableError with code \"github_attachment_too_large\" as soon as the running size exceeds MAX_ATTACHMENT_BYTES. This mid-stream guard catches files whose size wasn't known up front (missing or lying Content-Length), aborting the download so oversized payloads never buffer in memory.","triggerScenarios":"Downloading an attachment whose accumulated chunk bytes exceed MAX_ATTACHMENT_BYTES; also triggered earlier (line 780-784) when a numeric Content-Length header already exceeds the cap.","commonSituations":"Users attaching large videos or high-resolution images to GitHub issues; Content-Length absent due to chunked transfer so the stream guard is the only check; proxies stripping headers; misjudging the attachment cap when testing with large fixtures.","solutions":["Use a smaller attachment that fits within MAX_ATTACHMENT_BYTES","Check the declared Content-Length before download and reject early with the same error","Do not disable or raise MAX_ATTACHMENT_BYTES without reviewing memory limits","Serve a pre-resized/compressed image (GitHub renders user-attachments images, but raw bytes still count)"],"exampleFix":"// before: attaching a 30MB video as a chat attachment\nawait prepareGitHubPublicAttachment({ url: bigVideoUrl });\n// after: pre-check size and skip/notify\nif (contentLength > MAX_ATTACHMENT_BYTES) return rejectAttachment(\"github_attachment_too_large\");","handlingStrategy":"validation","validationCode":"const head = await fetch(attachmentUrl, { method: \"HEAD\" }); const len = Number(head.headers.get(\"content-length\") ?? 0); if (len > MAX_ATTACHMENT_BYTES) throw new Error(\"attachment exceeds size cap\");","typeGuard":"null","tryCatchPattern":"try { return await prepareGitHubPublicAttachment(attachment, signal); } catch (e) { if (e?.code === \"github_attachment_too_large\") { return oversizedNotice(attachment); } throw e; }","preventionTips":["Check Content-Length via HEAD before attempting download","Enforce size limits at attachment upload time, not only download time","Compress or downscale images before attaching","Keep MAX_ATTACHMENT_BYTES aligned with downstream memory budgets"],"tags":["github","attachments","file-size","limits"],"backgroundTag":"file-size-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"}