toeverything/AFFiNE · warning · ImageFormatNotSupported

image_format_not_supported

image_format_not_supported

Error message

Image format not supported: ${format}

What it means

Thrown by the uploadAvatar mutation when sniffMime() cannot identify the uploaded buffer's MIME type, or when the detected type does not start with 'image/'. This is the first image validation gate, run against the raw bytes (the declared mimetype is only a fallback hint for sniffing). Non-image uploads are rejected before any processing.

Source

Thrown at packages/backend/server/src/core/user/resolver.ts:128

    name: 'uploadAvatar',
    description: 'Upload user avatar',
  })
  async uploadAvatar(
    @CurrentUser() user: CurrentUser,
    @Args({ name: 'avatar', type: () => GraphQLUpload })
    avatar: FileUpload
  ) {
    if (!user) {
      throw new UserNotFound();
    }

    const avatarBuffer = await readBufferWithLimit(
      avatar.createReadStream(),
      5 * OneMB
    );
    const contentType = sniffMime(avatarBuffer, avatar.mimetype)?.toLowerCase();
    if (!contentType || !contentType.startsWith('image/')) {
      throw new ImageFormatNotSupported({ format: contentType || 'unknown' });
    }

    let processedAvatarBuffer: Buffer;
    try {
      processedAvatarBuffer = await processImage(avatarBuffer, 512, false);
    } catch {
      throw new ImageFormatNotSupported({ format: contentType });
    }

    const avatarUrl = await this.storage.put(
      `${user.id}-avatar-${Date.now()}`,
      processedAvatarBuffer,
      { contentType: 'image/webp' }
    );

    if (user.avatarUrl) {
      await this.storage.delete(user.avatarUrl);
    }

View on GitHub (pinned to 26c515e050)

Solutions

  1. Validate the file type on the client before uploading (accept='image/*' and check the actual type).
  2. Ensure the upload is not truncated by body-size limits in the proxy/gateway.
  3. If SVG should be allowed, confirm sniffMime/processImage support it; otherwise restrict the picker to raster formats.

Example fix

// before
<input type="file" />

// after
<input type="file" accept="image/png,image/jpeg,image/webp,image/gif" />
Defensive patterns

Strategy: validation

Validate before calling

const type = await sniffType(file);
if (!type.startsWith('image/')) { alert('Please choose an image file.'); return; }

Type guard

function isImageType(type?: string | null): boolean {
  return Boolean(type && type.startsWith('image/'));
}

Try / catch

try {
  await uploadAvatar(file);
} catch (e) {
  if (e?.code === 'image_format_not_supported') { showFormatError(); return; }
  throw e;
}

Prevention

When it happens

Trigger: Client uploads a file whose magic bytes are not a recognized image format, or whose sniffed type is application/pdf, text/*, etc. Also fires when the buffer is empty or truncated so sniffing fails.

Common situations: User selects a PDF, SVG (if not sniffed as image), or text file instead of a photo; upload truncated by a proxy limit; client declares image/png but sends unrelated bytes.

Related errors


AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12). Data as JSON: /api/errors/87891d5ba5135d55. Report an issue: GitHub.