unslothai/unsloth · error · HTTPException

Image '{original_name}' is too large; maximum is {_MAX_TRAIN

Error message

Image '{original_name}' is too large; maximum is {_MAX_TRAINING_IMAGE_SIDE}px per side.

What it means

HTTP 400 from _validate_uploaded_training_image: Pillow raised Image.DecompressionBombError while opening the uploaded file, meaning the image header declares dimensions past Pillow's ~179-megapixel bomb limit, so the check could not even read .size. The route converts this into a clean 400 (max 4096px per side) instead of an unhandled 500, since DecompressionBombError derives straight from Exception. Only the header is read — no pixels are decoded.

Source

Thrown at studio/backend/routes/training.py:3649

# Match diffusion's 4096px decoded-image limit.
_MAX_TRAINING_IMAGE_SIDE = 4096


def _validate_uploaded_training_image(path: Path, original_name: str) -> None:
    """Reject an uploaded training image whose decoded dimensions exceed the per-side limit.

    Reads only the header (never img.load()), so a small-payload / huge-dimension file is caught
    before it spikes memory. Bytes PIL cannot identify are left as-is (the upload contract accepts
    arbitrary bytes under an image extension), so only oversized real images change behaviour."""
    from PIL import Image, UnidentifiedImageError

    try:
        with Image.open(path) as image:
            width, height = image.size
    except Image.DecompressionBombError:
        # Past Pillow's ~179 MP limit Image.open() raises before .size can be read, with an error deriving straight from Exception, so letting it escape would 500 the upload.
        raise HTTPException(
            status_code = 400,
            detail = (
                f"Image '{original_name}' is too large; maximum is "
                f"{_MAX_TRAINING_IMAGE_SIDE}px per side."
            ),
        )
    except (OSError, UnidentifiedImageError, ValueError):
        return  # not a decodable image -> not a bomb; leave the existing contract
    if width > _MAX_TRAINING_IMAGE_SIDE or height > _MAX_TRAINING_IMAGE_SIDE:
        raise HTTPException(
            status_code = 400,
            detail = (
                f"Image '{original_name}' is too large ({width}x{height}); maximum is "
                f"{_MAX_TRAINING_IMAGE_SIDE}px per side."
            ),
        )

View on GitHub (pinned to 203007d190)

Solutions

  1. Reject/replace the offending image (the message names the file).
  2. Re-encode the image at sane dimensions (<=4096px per side): pngcrush/magick convert with resize, or strip bogus metadata.
  3. If a whole scraped dataset contains many bombs, batch-normalize it before upload: magick mogrify -resize '4096x4096>' *.png.

Example fix

# re-encode oversized images before upload
magick identify -format '%f %wx%h\n' *.png | awk '$2+0>4096 || $3+0>4096'  # find offenders
magick big.png -resize '4096x4096>' big.png
Defensive patterns

Strategy: validation

Validate before calling

from PIL import Image

def safe_to_upload(path: str) -> bool:
    try:
        with Image.open(path) as im:
            w, h = im.size
        return w <= 4096 and h <= 4096
    except Exception:
        return False  # let the server's non-decodable contract decide

Type guard

def is_image_dimension_error(exc: HTTPException) -> bool:
    return exc.status_code == 400 and 'too large' in exc.detail

Try / catch

try:
    resp = await client.post(upload_url, files=batch)
except httpx.HTTPStatusError as e:
    if e.response.status_code == 400 and 'too large' in e.response.json()['detail']:
        skip_and_log_offender(e.response.json()['detail'])
        continue
    raise

Prevention

When it happens

Trigger: Uploading (in a diffusion dataset batch) an image file under an image extension (.png/.jpg/.jpeg/.webp/.bmp) whose header claims width*height > ~179 MP. A tiny 50 KB PNG declaring 20000x20000 pixels triggers it during the upload's per-image validation pass.

Common situations: Malicious or accidental decompression bombs in scraped datasets; AI-upscaler outputs with huge dimension metadata; corrupt files with bogus headers; testing tools that push extreme dimensions.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/1dae671d455d89f8. Report an issue: GitHub.