jamiepine/voicebox · warning · ValueError

Invalid format '{img_format}'. Allowed formats: PNG, JPEG, W

Error message

Invalid format '{img_format}'. Allowed formats: PNG, JPEG, WEBP

What it means

Returned by validate_image() when PIL successfully loads the image but its normalized format is not in {PNG, JPEG, WEBP}. MPO and JPG are normalized to JPEG before the check. upload_avatar() re-raises this message as a ValueError. The allow-list keeps avatar storage predictable and avoids rarely-supported formats.

Source

Thrown at backend/services/profiles.py:649

) -> VoiceProfileResponse:
    """
    Upload and process avatar image for a profile.

    Args:
        profile_id: Profile ID
        image_path: Path to uploaded image file
        db: Database session

    Returns:
        Updated profile
    """
    profile = db.query(DBVoiceProfile).filter_by(id=profile_id).first()
    if not profile:
        raise ValueError(f"Profile {profile_id} not found")

    is_valid, error_msg = validate_image(image_path)
    if not is_valid:
        raise ValueError(error_msg)

    if profile.avatar_path:
        old_avatar = config.resolve_storage_path(profile.avatar_path)
        if old_avatar is not None and old_avatar.exists():
            old_avatar.unlink()

    # Determine file extension from uploaded file
    from PIL import Image

    with Image.open(image_path) as img:
        # Normalize JPEG variants (MPO is multi-picture format from some cameras)
        img_format = img.format
        if img_format in ("MPO", "JPG"):
            img_format = "JPEG"

        ext_map = {"PNG": ".png", "JPEG": ".jpg", "WEBP": ".webp"}
        ext = ext_map.get(img_format, ".png")

View on GitHub (pinned to 51f49dea19)

Solutions

  1. Convert the image to PNG, JPEG, or WEBP before uploading.
  2. On the client, restrict the file picker accept attribute to image/png,image/jpeg,image/webp.
  3. If you genuinely need another format, add it to ALLOWED_FORMATS and the post-normalization set in backend/utils/images.py (and confirm process_avatar handles it).
  4. For HEIC, install pillow-heif and normalize to JPEG.

Example fix

// before: uploading avatar.gif
// after: convert to PNG
from PIL import Image
Image.open('avatar.gif').convert('RGB').save('avatar.png', 'PNG')
Defensive patterns

Strategy: validation

Validate before calling

from PIL import Image

def is_allowed_image_format(path: str) -> bool:
    with Image.open(path) as img:
        fmt = img.format
        if fmt in ('MPO', 'JPG'):
            fmt = 'JPEG'
        return fmt in {'PNG', 'JPEG', 'WEBP'}

Try / catch

try:
    profile = await upload_avatar(profile_id, image_path, db)
except ValueError as e:
    if 'Invalid format' in str(e):
        raise HTTPException(415, str(e))
    raise

Prevention

When it happens

Trigger: Uploading a GIF, BMP, TIFF, WEBP-variant, or HEIC file as an avatar; a file with a misleading extension whose actual PIL-detected format is unsupported.

Common situations: Users exporting from design tools to GIF/BMP; screenshots saved as TIFF; HEIC photos from iPhones that PIL reads as HEIF (requires plugins); animated stickers in GIF form.

Related errors


AI-assisted analysis of jamiepine/voicebox@51f49dea19 (2026-08-12). Data as JSON: /api/errors/eddf01a035e351e1. Report an issue: GitHub.