{"record":{"id":"c854f2eca5bdd7c2","repo":"toeverything/AFFiNE","slug":"image-format-not-supported-c854f2","errorCode":"image_format_not_supported","errorMessage":"Image format not supported: ${format}","messagePattern":"Image format not supported: (.+?)","errorType":"exception","errorClass":"ImageFormatNotSupported","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/plugins/copilot/conversation/inbox.ts","lineNumber":98,"sourceCode":"\n    for (const blob of blobs) {\n      const uploaded = await this.storage.handleUpload(userId, blob);\n      const detectedMime =\n        sniffMime(uploaded.buffer, blob.mimetype)?.toLowerCase() ||\n        blob.mimetype;\n      let attachmentBuffer = uploaded.buffer;\n      let attachmentMimeType = detectedMime;\n\n      if (detectedMime.startsWith('image/')) {\n        try {\n          attachmentBuffer = await processImage(\n            uploaded.buffer,\n            COPILOT_IMAGE_MAX_EDGE,\n            true\n          );\n          attachmentMimeType = 'image/webp';\n        } catch {\n          throw new ImageFormatNotSupported({ format: detectedMime });\n        }\n      }\n\n      const filename = createHash('sha256')\n        .update(attachmentBuffer)\n        .digest('base64url');\n      const attachment = await this.storage.put(\n        userId,\n        session.config.workspaceId,\n        filename,\n        attachmentBuffer\n      );\n      attachments.push({\n        kind: 'url',\n        url: attachment,\n        mimeType: attachmentMimeType,\n        fileName: blob.filename,\n      });","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/b4c8548c09da21b2898443559a5b846f0ccf5dd8/packages/backend/server/src/plugins/copilot/conversation/inbox.ts#L80-L116","documentation":"Thrown while processing an uploaded image attachment: the buffer's detected MIME type starts with 'image/', but processImage (resize to COPILOT_IMAGE_MAX_EDGE, convert to webp) rejected or crashed on the bytes. It wraps the failure as ImageFormatNotSupported with error code 'image_format_not_supported', naming the detected MIME in {format}. Typical causes are corrupted payloads, CMYJ/EXIF-heavy JPEGs, or exotic formats that the decoder cannot handle even though sniffing labeled them image/*.","triggerScenarios":"Uploading a truncated or corrupted image file; an image whose extension/sniffed MIME says image/* but whose codec is unsupported (e.g. HEIC mislabeled, TIFF with odd compression, 16-bit PNG edge cases); a zero-byte or text file renamed to .png so the sniffer misclassifies it.","commonSituations":"Paste/drag from clipboard produces partial buffer; client-side compression library emits nonstandard webp/jpeg; users rename files to bypass type checks.","solutions":["Re-encode the image client-side to standard JPEG/PNG/WebP before upload","Verify the file opens in an image viewer and is not truncated (check byte size > 0 and matches source)","Retry the upload — transient read/truncation during transfer can corrupt the buffer","If maintaining this code: narrow the catch to decoder-specific errors and let unexpected failures surface separately"],"exampleFix":"// before\nawait inbox.createMessage(userId, { sessionId, blob: corruptedBuffer });\n\n// after\ntry {\n  const webp = await processImage(buffer, maxEdge, true);\n  await inbox.createMessage(userId, { sessionId, blob: webp, blobMimeType: 'image/webp' });\n} catch {\n  showUserError('This image format is not supported. Please use PNG, JPEG or WebP.');\n}","handlingStrategy":"validation","validationCode":"const sniffed = await imageType(buffer); // e.g. file-type\nconst SUPPORTED = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];\nif (!sniffed || !SUPPORTED.includes(sniffed.mime)) {\n  return rejectUpload('Use PNG, JPEG, WebP or GIF');\n}\nconst probe = await loadImage(buffer); // throws on truncated/unsupported bytes\nif (buffer.length === 0) return rejectUpload('Empty file');","typeGuard":"const isDecodableImage = async (buf: Buffer): Promise<boolean> => {\n  try { await sharp(buf).metadata(); return true; } catch { return false; }\n};","tryCatchPattern":"try {\n  await inbox.createMessage(userId, { sessionId, blob });\n} catch (e) {\n  if (e.code === 'image_format_not_supported') {\n    notifyUser(`Unsupported image format: ${e.format ?? 'unknown'}. Re-encode as PNG/JPEG/WebP.`);\n  } else throw e;\n}","preventionTips":["Client-side pre-encode uploads to a standard format and verify decodability before sending","Check file size > 0 and matches the source after any transfer/compression step","Surface the reported {format} to the user so they know which file failed"],"tags":["copilot","image-processing","upload","validation"],"backgroundTag":"unsupported-media-type","analyzedSha":"b4c8548c09da21b2898443559a5b846f0ccf5dd8","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}