Wei-Shaw/sub2api · warning

profile.avatar.gifTooLarge

Error message

profile.avatar.gifTooLarge

What it means

In frontend/src/components/user/profile/ProfileAvatarCard.vue:197, prepareAvatarUpload() throws the localized 'profile.avatar.gifTooLarge' for image/gif files whose size exceeds targetAvatarUploadBytes. GIFs are passed through untouched (no canvas recompression, since re-encoding would kill animation), so oversized GIFs are rejected outright.

Source

Thrown at frontend/src/components/user/profile/ProfileAvatarCard.vue:197

    for (const quality of avatarQualitySteps) {
      const blob = await canvasToBlob(canvas, 'image/webp', quality)
      if (blob.size <= targetAvatarUploadBytes) {
        const fileName = file.name.replace(/\.[^.]+$/, '') || 'avatar'
        return new File([blob], `${fileName}.webp`, { type: 'image/webp' })
      }
    }
  }

  throw new Error(t('profile.avatar.compressTooLarge'))
}

async function prepareAvatarUpload(file: File): Promise<File> {
  if (!file.type.startsWith('image/')) {
    throw new Error(t('profile.avatar.invalidType'))
  }
  if (file.type === 'image/gif') {
    if (file.size > targetAvatarUploadBytes) {
      throw new Error(t('profile.avatar.gifTooLarge'))
    }
    return file
  }
  if (file.size <= targetAvatarUploadBytes) {
    return file
  }
  return compressAvatarFile(file)
}

async function handleAvatarFileChange(event: Event) {
  const input = event.target as HTMLInputElement | null
  const file = input?.files?.[0]
  if (input) {
    input.value = ''
  }
  if (!file) {
    return
  }

View on GitHub (pinned to 073e92d171)

Solutions

  1. Show the actual size limit in the UI next to the file picker so users know the GIF budget before selecting.
  2. Optionally offer server-side or wasm-based GIF downscaling (gifsicle) instead of a hard rejection.
  3. Increase targetAvatarUploadBytes for gif specifically if the backend accepts it.
  4. Client-side pre-check file.size before the picker even opens (some browsers support showOpenFilePicker with size hints via UI copy).

Example fix

// before
if (file.type === 'image/gif') {
  if (file.size > targetAvatarUploadBytes) {
    throw new Error(t('profile.avatar.gifTooLarge'))
  }
  return file
}

// after
if (file.type === 'image/gif') {
  if (file.size > targetAvatarUploadBytes) {
    throw new Error(t('profile.avatar.gifTooLarge', { max: formatBytes(targetAvatarUploadBytes) }))
  }
  return file
}
Defensive patterns

Strategy: validation

Validate before calling

if (file.type === 'image/gif' && file.size > targetAvatarUploadBytes) {
  showGifSizeError(formatBytes(targetAvatarUploadBytes)); // before any upload attempt
  return;
}

Prevention

When it happens

Trigger: Selecting an animated GIF larger than targetAvatarUploadBytes (the upload budget). The file passes the image/* check and enters the gif branch, where only the size gate applies.

Common situations: Users uploading full-resolution animated stickers exported from messaging apps; a low server avatar size limit; users expecting the app to auto-compress GIFs like it does JPEG/PNG.

Related errors


AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15). Data as JSON: /api/errors/4264590466b9fd08. Report an issue: GitHub.