toeverything/AFFiNE · error · ImageFormatNotSupported

image_format_not_supported

image_format_not_supported

Error message

Image format not supported: ${format}

What it means

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/*.

Source

Thrown at packages/backend/server/src/plugins/copilot/conversation/inbox.ts:98

    for (const blob of blobs) {
      const uploaded = await this.storage.handleUpload(userId, blob);
      const detectedMime =
        sniffMime(uploaded.buffer, blob.mimetype)?.toLowerCase() ||
        blob.mimetype;
      let attachmentBuffer = uploaded.buffer;
      let attachmentMimeType = detectedMime;

      if (detectedMime.startsWith('image/')) {
        try {
          attachmentBuffer = await processImage(
            uploaded.buffer,
            COPILOT_IMAGE_MAX_EDGE,
            true
          );
          attachmentMimeType = 'image/webp';
        } catch {
          throw new ImageFormatNotSupported({ format: detectedMime });
        }
      }

      const filename = createHash('sha256')
        .update(attachmentBuffer)
        .digest('base64url');
      const attachment = await this.storage.put(
        userId,
        session.config.workspaceId,
        filename,
        attachmentBuffer
      );
      attachments.push({
        kind: 'url',
        url: attachment,
        mimeType: attachmentMimeType,
        fileName: blob.filename,
      });

View on GitHub (pinned to b4c8548c09)

Solutions

  1. Re-encode the image client-side to standard JPEG/PNG/WebP before upload
  2. Verify the file opens in an image viewer and is not truncated (check byte size > 0 and matches source)
  3. Retry the upload — transient read/truncation during transfer can corrupt the buffer
  4. If maintaining this code: narrow the catch to decoder-specific errors and let unexpected failures surface separately

Example fix

// before
await inbox.createMessage(userId, { sessionId, blob: corruptedBuffer });

// after
try {
  const webp = await processImage(buffer, maxEdge, true);
  await inbox.createMessage(userId, { sessionId, blob: webp, blobMimeType: 'image/webp' });
} catch {
  showUserError('This image format is not supported. Please use PNG, JPEG or WebP.');
}
Defensive patterns

Strategy: validation

Validate before calling

const sniffed = await imageType(buffer); // e.g. file-type
const SUPPORTED = ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
if (!sniffed || !SUPPORTED.includes(sniffed.mime)) {
  return rejectUpload('Use PNG, JPEG, WebP or GIF');
}
const probe = await loadImage(buffer); // throws on truncated/unsupported bytes
if (buffer.length === 0) return rejectUpload('Empty file');

Type guard

const isDecodableImage = async (buf: Buffer): Promise<boolean> => {
  try { await sharp(buf).metadata(); return true; } catch { return false; }
};

Try / catch

try {
  await inbox.createMessage(userId, { sessionId, blob });
} catch (e) {
  if (e.code === 'image_format_not_supported') {
    notifyUser(`Unsupported image format: ${e.format ?? 'unknown'}. Re-encode as PNG/JPEG/WebP.`);
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: Paste/drag from clipboard produces partial buffer; client-side compression library emits nonstandard webp/jpeg; users rename files to bypass type checks.

Related errors


AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18). Data as JSON: /api/errors/c854f2eca5bdd7c2. Report an issue: GitHub.