odysseus-dev/odysseus · error · HTTPException

Pillow is required for MLX image edit bridge masks.

Error message

Pillow is required for MLX image edit bridge masks.

What it means

Raised by _write_bridge_mask in scripts/mlx_image_server.py when importing PIL fails while normalizing the uploaded mask PNG for the inpainting bridge (sibling of the input-image Pillow check). Without Pillow the server cannot apply the OpenAI alpha-transparency mask convention, so it returns HTTP 503.

Solutions

  1. pip install Pillow in the server's launch environment
  2. Verify import with the exact interpreter (python -c 'from PIL import Image')
  3. Reinstall Pillow if the import fails with an OSError about the C extension
Defensive patterns

Strategy: type-guard

Validate before calling

import importlib.util
assert importlib.util.find_spec('PIL'), 'install Pillow before serving edits'

Type guard

def has_pillow() -> bool:
    import importlib.util
    return importlib.util.find_spec('PIL') is not None

Prevention

When it happens

Trigger: POST /v1/images/edits with a mask file against a LaMa/MI-GAN model when the launch Python cannot import PIL.

Common situations: Same as 943: server venv missing Pillow, wrong interpreter used at launch, or a broken Pillow install; often both 943 and 945 trigger together since the input image is written before the mask.

Related errors


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

Appendix: source

Thrown at scripts/mlx_image_server.py:191

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:
        raise HTTPException(400, f"Invalid mask image: {e}") from e


def _run_bridge(cmd: list[str]) -> None:
    env = os.environ.copy()
    proc = subprocess.run(cmd, env=env, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    if proc.returncode != 0:
        detail = (proc.stderr or proc.stdout or "MLX Swift bridge failed").strip()

View on GitHub (pinned to f9235ebbf1)