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
- Reject/replace the offending image (the message names the file).
- Re-encode the image at sane dimensions (<=4096px per side): pngcrush/magick convert with resize, or strip bogus metadata.
- 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
- Pre-scan dataset images' headers with PIL before uploading; skip anything over 4096px per side.
- Keep Pillow's MAX_IMAGE_PIXELS at its default; do not disable the bomb check.
- Validate third-party/scraped datasets before they reach the trainer.
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
- Image '{original_name}' is too large ({width}x{height}); max
- Could not decode image: {exc}
- Local commands aren't enabled on this server. To allow them,
- Dataset '{cleaned}' must not be a symbolic link.
- Dataset '{cleaned}' escapes the Studio datasets directory.
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/1dae671d455d89f8.
Report an issue: GitHub.