immich-app/immich · error · HTTPException

Image has zero width or height

Error message

Image has zero width or height

What it means

Raised as HTTPException(400) by the /predict endpoint when the uploaded image decodes successfully (PIL) but has width==0 or height==0. The decode succeeds but the resulting image is degenerate and cannot be used for inference.

Source

Thrown at machine-learning/immich_ml/main.py:175

async def root() -> ORJSONResponse:
    return ORJSONResponse({"message": "Immich ML"})


@app.get("/ping")
def ping() -> PlainTextResponse:
    return PlainTextResponse("pong")


@app.post("/predict", dependencies=[Depends(update_state)])
async def predict(
    entries: InferenceEntries = Depends(get_entries),
    image: bytes | None = File(default=None),
    text: str | None = Form(default=None),
) -> Any:
    if image is not None:
        decoded = await run(lambda: decode_pil(image))
        if decoded.width == 0 or decoded.height == 0:
            raise HTTPException(400, "Image has zero width or height")
        inputs: Image | str = decoded
    elif text is not None:
        inputs = text
    else:
        raise HTTPException(400, "Either image or text must be provided")
    response = await run_inference(inputs, entries)
    return ORJSONResponse(response)


async def run_inference(payload: Image | str, entries: InferenceEntries) -> InferenceResponse:
    outputs: dict[ModelIdentity, Any] = {}
    response: InferenceResponse = {}

    async def _run_inference(entry: InferenceEntry) -> None:
        model = await model_cache.get(
            entry["name"], entry["type"], entry["task"], ttl=settings.model_ttl, **entry["options"]
        )
        inputs = [payload]

View on GitHub (pinned to 199723261c)

Solutions

  1. Re-upload or re-encode the source image so it has valid non-zero dimensions.
  2. On the server side, validate the asset file integrity (e.g. re-run metadata extraction) before queuing ML jobs.
  3. If only a few assets are affected, exclude/regenerate them via the CLIP/duplicate jobs after fixing the file.

Example fix

# before
img = PILImage.open(path)
img.load()  # may yield 0x0

# after
img = PILImage.open(path)
if not img.width or not img.height:
    raise ValueError(f'bad dimensions {img.size} for {path}')
Defensive patterns

Strategy: validation

Validate before calling

from PIL import Image
def valid_image(b: bytes) -> bool:
    try:
        im = Image.open(io.BytesIO(b)); im.load()
        return im.width > 0 and im.height > 0
    except Exception:
        return False

Type guard

def has_valid_dimensions(im) -> bool:
    return getattr(im, 'width', 0) > 0 and getattr(im, 'height', 0) > 0

Try / catch

try:
    decoded = await run(lambda: decode_pil(image))
except Exception:
    raise HTTPException(400, 'Image could not be decoded')

Prevention

When it happens

Trigger: POST /predict with an image file that PIL can decode but which has zero dimension(s) — a corrupt/truncated image header reporting 0x0, or an intentionally crafted edge-case image.

Common situations: Corrupt upload from a flaky mobile sync; partially-written file on disk before the ML request; re-encoded by a faulty transcode/thumbnail pipeline; a 0-byte or header-only file that some decoder tolerates.

Related errors


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/7ba158ca3dfc78fa. Report an issue: GitHub.