{"record":{"id":"87891d5ba5135d55","repo":"toeverything/AFFiNE","slug":"image-format-not-supported","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/core/user/resolver.ts","lineNumber":130,"sourceCode":"    name: 'uploadAvatar',\n    description: 'Upload user avatar',\n  })\n  async uploadAvatar(\n    @CurrentUser() user: CurrentUser,\n    @Args({ name: 'avatar', type: () => GraphQLUpload })\n    avatar: FileUpload\n  ) {\n    if (!user) {\n      throw new UserNotFound();\n    }\n\n    const avatarBuffer = await readBufferWithLimit(\n      avatar.createReadStream(),\n      5 * OneMB\n    );\n    const contentType = sniffMime(avatarBuffer, avatar.mimetype)?.toLowerCase();\n    if (!contentType || !contentType.startsWith('image/')) {\n      throw new ImageFormatNotSupported({ format: contentType || 'unknown' });\n    }\n\n    let processedAvatarBuffer: Buffer;\n    try {\n      processedAvatarBuffer = await processImage(avatarBuffer, 512, false);\n    } catch {\n      throw new ImageFormatNotSupported({ format: contentType });\n    }\n\n    const avatarUrl = await this.storage.put(\n      `${user.id}-avatar-${Date.now()}`,\n      processedAvatarBuffer,\n      { contentType: 'image/webp' }\n    );\n\n    if (user.avatarUrl) {\n      await this.storage.delete(user.avatarUrl);\n    }","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/2af30773aecd567f09b346e7b72fc69143144057/packages/backend/server/src/core/user/resolver.ts#L112-L148","documentation":"Avatar uploads sniff the file's magic bytes, cross-checking the declared mimetype. If sniffing cannot identify a type at all, or the identified type does not start with 'image/', the upload is rejected with image_format_not_supported ('unknown' when sniffing failed) before any image processing runs.","triggerScenarios":"Uploading a non-image (pdf, zip, text) renamed to .png; an empty or truncated file whose bytes cannot be sniffed; formats the sniffer does not recognize.","commonSituations":"Automated imports feeding arbitrary files; drag-and-drop grabbing the wrong file; mislabeled files produced by other tooling.","solutions":["Upload actual raster images (png/jpeg/webp/gif)","Validate the file type client-side (extension + magic bytes) before starting the upload","Convert exotic formats to png/webp before uploading"],"exampleFix":"// before\nawait uploadAvatar(file); // pdf renamed to .png -> image_format_not_supported\n\n// after\nif (!file.type.startsWith('image/')) throw new Error('pick an image file');\nawait uploadAvatar(file);","handlingStrategy":"validation","validationCode":"// client-side gate before upload\nif (!file.type.startsWith('image/')) {\n  throw new Error('please choose an image file');\n}\nif (file.size > 5 * 1024 * 1024) {\n  throw new Error('image must be under 5MB');\n}","typeGuard":"function isImageFile(file: File): boolean {\n  return file.type.startsWith('image/');\n}","tryCatchPattern":"try {\n  await uploadAvatar(file);\n} catch (e) {\n  if (e?.extensions?.code === 'IMAGE_FORMAT_NOT_SUPPORTED') showFormatError();\n  else throw e;\n}","preventionTips":["Accept only image/* in the file picker (accept attribute) and re-check the type in code","Remember the server sniffs magic bytes, so renaming extensions will not pass","Convert spreadsheets/screenshots-in-pdf etc. to real images before upload"],"tags":["upload","avatar","file-type","validation"],"backgroundTag":"unsupported-image-format","analyzedSha":"2af30773aecd567f09b346e7b72fc69143144057","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}