unslothai/unsloth · error · HTTPException

Image '{original_name}' is too large ({width}x{height}); max

Error message

Image '{original_name}' is too large ({width}x{height}); maximum is {_MAX_TRAINING_IMAGE_SIDE}px per side.

What it means

HTTP 400 from _validate_uploaded_training_image: the image header was read successfully (no bomb error) and either width or height exceeds _MAX_TRAINING_IMAGE_SIDE (4096px, matching diffusion's limit). The specific measured dimensions are reported. Only the header is read, so the check is cheap; non-decodable bytes are intentionally passed through (the upload contract accepts arbitrary bytes under an image extension).

Source

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

    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."
            ),
        )


def _safe_dataset_image_path(folder: Path, filename: str) -> Path:
    """Resolve ``filename`` to an image path strictly inside ``folder``. Rejects any path
    separators / traversal / null bytes and non-image extensions."""
    raw = filename or ""
    if "/" in raw or "\\" in raw or ".." in raw or "\x00" in raw or raw != Path(raw).name:
        raise HTTPException(status_code = 400, detail = "Invalid image filename.")
    if Path(raw).suffix.lower() not in _DIFFUSION_DATASET_IMAGE_EXTS:
        exts = ", ".join(sorted(_DIFFUSION_DATASET_IMAGE_EXTS))
        raise HTTPException(status_code = 400, detail = f"Not an image file. Allowed: {exts}")
    path = folder / raw

View on GitHub (pinned to 203007d190)

Solutions

  1. Downscale the image so both sides are <=4096px (e.g. magick convert in.png -resize '4096x4096>' out.png) and re-upload that file.
  2. Batch-normalize whole folders: magick mogrify -resize '4096x4096>' *.png.
  3. Configure your export/save pipeline (camera export, scanner, upscaler) to cap the long edge at 4096.

Example fix

// before
img.save('train.png')  // 6000x4000 -> 400 on upload

// after
img.thumbnail((4096, 4096))  # in-place, preserves aspect
img.save('train.png')
Defensive patterns

Strategy: validation

Validate before calling

from PIL import Image

def within_side_limit(path: str, limit: int = 4096) -> bool:
    with Image.open(path) as im:
        w, h = im.size
    return w <= limit and h <= limit

Type guard

def is_dimension_reject(exc: HTTPException) -> bool:
    return exc.status_code == 400 and 'px per side' in exc.detail

Try / catch

try:
    await upload(files=[f])
except UploadRejected as e:  # wraps HTTPException detail
    if 'px per side' in e.detail:
        downscale_and_retry(f, 4096)
    else:
        raise

Prevention

When it happens

Trigger: Uploading a diffusion-dataset image whose real dimensions exceed 4096 on either side, e.g. a 6000x4000 photo exported at full resolution from a camera or a 8000px AI upscale.

Common situations: Raw camera exports (modern sensors exceed 4096 on the long edge); print-resolution scans; upscaled wallpapers; intermediate training outputs re-imported at full size.

Related errors


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