odysseus-dev/odysseus · error · HTTPException

Invalid input image: {e}

Error message

Invalid input image: {e}

What it means

Raised by _write_bridge_input_image in scripts/mlx_image_server.py when PIL fails to open, convert to RGBA, or PNG-save the uploaded edit image (any exception inside the try block is chained into HTTP 400). It signals malformed or unsupported image bytes rather than a server fault.

Source

Thrown at scripts/mlx_image_server.py:183

    if snap.is_file():
        return snap
    candidates = sorted(snap.rglob("*.safetensors"))
    if not candidates:
        raise HTTPException(500, f"No safetensors weights found for {model} in {snap}")
    return candidates[0]


def _write_bridge_input_image(raw: bytes, out_path: Path) -> None:
    try:
        from PIL import Image
        import io
    except Exception as e:
        raise HTTPException(503, "Pillow is required for MLX image edit bridge inputs.") from e
    try:
        img = Image.open(io.BytesIO(raw)).convert("RGBA")
        img.save(out_path, format="PNG")
    except Exception as e:
        raise HTTPException(400, f"Invalid input image: {e}") from e


def _write_bridge_mask(raw: bytes, out_path: Path) -> None:
    try:
        from PIL import Image
        import io
    except Exception as e:
        raise HTTPException(503, "Pillow is required for MLX image edit bridge masks.") from e
    try:
        img = Image.open(io.BytesIO(raw))
        if img.mode == "RGBA":
            # OpenAI edits mask convention: transparent = regenerate.
            alpha = img.getchannel("A")
            mask = alpha.point(lambda p: 255 if p < 128 else 0)
        else:
            mask = img.convert("L")
        mask.save(out_path, format="PNG")
    except Exception as e:

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Validate the file locally before upload: open it with PIL and re-save as PNG
  2. Confirm the multipart field named 'image' actually contains the image and is fully transmitted
  3. If the file is HEIC/AVIF/etc., convert it to PNG or JPEG client-side first
  4. Check server temp directory is writable if uploads are known-good

Example fix

# before
files = {'image': open('possibly-broken.png','rb')}
# after
from PIL import Image
img = Image.open('possibly-broken.png'); img.verify()
files = {'image': open('known-good.png','rb')}
Defensive patterns

Strategy: validation

Validate before calling

from PIL import Image
import io
img = Image.open(io.BytesIO(raw_bytes)); img.verify()  # raises before upload if broken
img = Image.open(io.BytesIO(raw_bytes)).convert('RGB')
img.save('/tmp/out.png', format='PNG')  # exercise save too

Try / catch

try:
    r = requests.post(f'{base}/v1/images/edits', files=files)
    r.raise_for_status()
except requests.HTTPError as e:
    if e.response.status_code == 400:
        fix_and_recompress_image(); retry_once()

Prevention

When it happens

Trigger: Uploading a corrupted file, a non-image file (e.g. PDF/text renamed .png), a truncated upload, or an image format PIL cannot decode; occasionally an unwritable temp path makes img.save raise the same way.

Common situations: Client sends base64-decoded garbage or empty bytes; multipart field order sends the wrong file into 'image'; progressive/interrupted upload; image saved with a .png extension but actually WebP/HEIC and PIL lacks the decoder plugin.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/36f253720819e7d7. Report an issue: GitHub.