Wei-Shaw/sub2api · warning

profile.avatar.compressTooLarge

Error message

profile.avatar.compressTooLarge

What it means

In frontend/src/components/user/profile/ProfileAvatarCard.vue:188, compressAvatarFile() iterates a ladder of downscale steps (avatarScaleSteps) and webp quality steps (avatarQualitySteps); if no scale/quality combination produces a blob under targetAvatarUploadBytes, it throws the localized 'profile.avatar.compressTooLarge'. The image cannot be shrunk to the upload budget — typically a huge source image or a pathological image that webp encodes poorly.

Source

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

  for (const scale of avatarScaleSteps) {
    const width = Math.max(1, Math.round(image.naturalWidth * scale))
    const height = Math.max(1, Math.round(image.naturalHeight * scale))
    canvas.width = width
    canvas.height = height
    ctx.clearRect(0, 0, width, height)
    ctx.drawImage(image, 0, 0, width, height)

    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)
}

View on GitHub (pinned to 073e92d171)

Solutions

  1. Extend the scale ladder with smaller final scales (e.g., down to 128px) so the last step nearly always fits.
  2. Detect toBlob('image/webp') support (Safari fallback) and use 'image/jpeg' quality ladder as fallback output type.
  3. Reject early with a clear message when naturalWidth*smallestScale still can't plausibly fit, before doing the expensive loop.
  4. Raise targetAvatarUploadBytes if the product allows larger avatars.

Example fix

// before
for (const scale of avatarScaleSteps) {
  // ... for (const quality of avatarQualitySteps) { ... }
}
throw new Error(t('profile.avatar.compressTooLarge'))

// after
const outType = canvasToBlobSupportsWebp() ? 'image/webp' : 'image/jpeg'
for (const scale of avatarScaleSteps) {
  for (const quality of avatarQualitySteps) {
    const blob = await canvasToBlob(canvas, outType, quality)
    if (blob.size <= targetAvatarUploadBytes) return new File([blob], `${fileName}.${outType.split('/')[1]}`, { type: outType })
  }
}
throw new Error(t('profile.avatar.compressTooLarge'))
Defensive patterns

Strategy: validation

Validate before calling

// Reject implausibly huge images before the expensive compression ladder:
const minPixels = 128 * 128;
if (image.naturalWidth * image.naturalHeight < minPixels && file.size > targetAvatarUploadBytes * 2) {
  throw new Error(t('profile.avatar.compressTooLarge'));
}

Try / catch

try { avatar = await compressAvatarFile(file); }
catch (e) {
  if (e.message === t('profile.avatar.compressTooLarge')) { showSizeHint(); return; }
  throw e;
}

Prevention

When it happens

Trigger: Uploading a very high-resolution photo (e.g., 8000×6000) where even the smallest scale step keeps the encode above targetAvatarUploadBytes; images with noisy/photographic content that resist webp compression; browsers whose canvas.toBlob for image/webp falls back to png (older Safari), yielding larger blobs than expected.

Common situations: Panorama or RAW-export uploads; screenshots with gradients; Safari < 14 lacking solid webp encoding so every quality step returns similar sizes; targetAvatarUploadBytes set very low by configuration.

Related errors


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