{"record":{"id":"e93e96ac95eb24a8","repo":"paperclipai/paperclip","slug":"attachment-attachment-id-is-attachment-bytesi","errorCode":null,"errorMessage":"attachment ${attachment.id} is ${attachment.byteSize} bytes, over the ${maxBytes}-byte cap","messagePattern":"attachment (.+?) is (.+?) bytes, over the (.+?)-byte cap","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/src/services/plugin-host-services.ts","lineNumber":2597,"sourceCode":"        return { interaction: resolved as any, applied: true };\n      },\n      async listAttachments(params) {\n        const companyId = ensureCompanyId(params.companyId);\n        await ensurePluginAvailableForCompany(companyId);\n        if (!inCompany(await issues.getById(params.issueId), companyId)) return [];\n        return (await issues.listAttachments(params.issueId)) as any;\n      },\n      async getAttachmentContent(params) {\n        const companyId = ensureCompanyId(params.companyId);\n        await ensurePluginAvailableForCompany(companyId);\n        const attachment = await issues.getAttachmentById(params.attachmentId);\n        // Unknown and cross-company ids are deliberately indistinguishable to\n        // the plugin: both return null (no existence oracle across companies).\n        if (!attachment || attachment.companyId !== companyId) return null;\n\n        const maxBytes = typeof params.maxBytes === \"number\" && params.maxBytes > 0 ? params.maxBytes : null;\n        if (maxBytes !== null && attachment.byteSize > maxBytes) {\n          throw new Error(\n            `attachment ${attachment.id} is ${attachment.byteSize} bytes, over the ${maxBytes}-byte cap`,\n          );\n        }\n\n        const object = await getStorageService().getObject(attachment.companyId, attachment.objectKey);\n        const chunks: Buffer[] = [];\n        let total = 0;\n        for await (const chunk of object.stream) {\n          const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n          total += buf.length;\n          // Defense in depth: enforce the cap during streaming too, so a\n          // metadata/object size mismatch can never exceed the requested cap.\n          if (maxBytes !== null && total > maxBytes) {\n            object.stream.destroy();\n            throw new Error(`attachment ${attachment.id} exceeded the ${maxBytes}-byte cap while reading`);\n          }\n          chunks.push(buf);\n        }","sourceCodeStart":2579,"sourceCodeEnd":2615,"githubUrl":"https://github.com/paperclipai/paperclip/blob/a7e689b3c35347b529cb9f54c9b9a8575a3dcab6/server/src/services/plugin-host-services.ts#L2579-L2615","documentation":"getAttachmentContent was called with a maxBytes cap smaller than the attachment's stored byteSize. The host refuses to read the object so plugins cannot trick it into buffering oversized attachments.","triggerScenarios":"Thrown at server/src/services/plugin-host-services.ts:2588 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Reduce the attachment size below the byte cap shown in the error, or split the payload into multiple smaller attachments."],"exampleFix":null,"handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"a7e689b3c35347b529cb9f54c9b9a8575a3dcab6","analyzedAt":"2026-08-18T22:49:45.177Z","contentChangedAt":"2026-08-18T22:49:45.177Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}