{"record":{"id":"7b01f6b46736511d","repo":"toeverything/AFFiNE","slug":"comment-attachment-quota-exceeded","errorCode":"comment_attachment_quota_exceeded","errorMessage":"You have exceeded the comment attachment size quota.","messagePattern":"You have exceeded the comment attachment size quota\\.","errorType":"exception","errorClass":"CommentAttachmentQuotaExceeded","httpStatus":402,"severity":"error","filePath":"packages/backend/server/src/core/comment/resolver.ts","lineNumber":362,"sourceCode":"    description: 'Upload a comment attachment and return the access url',\n  })\n  async uploadCommentAttachment(\n    @CurrentUser() me: UserType,\n    @Args('workspaceId') workspaceId: string,\n    @Args('docId') docId: string,\n    @Args({ name: 'attachment', type: () => GraphQLUpload })\n    attachment: FileUpload\n  ) {\n    await this.assertPermission(\n      me,\n      { workspaceId, docId },\n      'Doc.Comments.Create'\n    );\n\n    const buffer = await readableToBuffer(attachment.createReadStream());\n    // max attachment size is 10MB\n    if (buffer.length > 10 * 1024 * 1024) {\n      throw new CommentAttachmentQuotaExceeded();\n    }\n\n    const checkExceeded =\n      await this.quota.getWorkspaceQuotaCalculator(workspaceId);\n    const result = checkExceeded(buffer.length);\n    if (result?.blobQuotaExceeded || result?.storageQuotaExceeded) {\n      throw new CommentAttachmentQuotaExceeded();\n    }\n\n    const key = randomUUID();\n    await this.commentAttachmentStorage.put(\n      workspaceId,\n      docId,\n      key,\n      attachment.filename ?? key,\n      buffer,\n      me.id\n    );","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/core/comment/resolver.ts#L344-L380","documentation":"Mutation uploadCommentAttachment (resolver.ts:361-363) buffers the whole upload and rejects it with CommentAttachmentQuotaExceeded when buffer.length exceeds 10 * 1024 * 1024 bytes. This is the hard per-file cap, enforced before the workspace quota calculator runs.","triggerScenarios":"Calling uploadCommentAttachment with a GraphQLUpload whose streamed bytes total more than 10 MiB - large screenshots, screen recordings, or PDFs attached to a comment.","commonSituations":"Attaching raw photos/videos from a phone; assuming the limit applies to compressed size (it is the raw buffered stream size); no client-side size check before upload.","solutions":["Compress or downscale the attachment below 10 MiB before uploading.","Add a client-side File.size > 10*1024*1024 check and block the upload early.","For bigger files, store them in doc cloud storage/drive instead of a comment attachment."],"exampleFix":"// before\nconst url = await uploadCommentAttachment(workspaceId, docId, file); // 25MB video -> error\n\n// after\nconst MAX = 10 * 1024 * 1024;\nif (file.size > MAX) throw new Error('Attachment must be under 10MB');\nconst url = await uploadCommentAttachment(workspaceId, docId, file);","handlingStrategy":"validation","validationCode":"const MAX_ATTACHMENT_BYTES = 10 * 1024 * 1024;\nif (file.size > MAX_ATTACHMENT_BYTES) {\n  alert('Attachment must be 10MB or smaller');\n  return;\n}\nconst url = await uploadCommentAttachment(workspaceId, docId, file);","typeGuard":null,"tryCatchPattern":"try {\n  await uploadCommentAttachment(workspaceId, docId, file);\n} catch (e) {\n  if (getErrorCode(e) === 'comment_attachment_quota_exceeded' && file.size > 10 * 1024 * 1024) {\n    // too big: compress/downscale and retry with the smaller file\n  } else throw e;\n}","preventionTips":["Check File.size against 10 MiB in the picker before any network call.","Compress images/video before attaching to comments.","The server measures the raw buffered stream, not the on-disk compressed size."],"tags":["comments","file-upload","size-limit","quota","graphql-upload"],"backgroundTag":"file-size-limit-exceeded","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}