odysseus-dev/odysseus · error · HTTPException

Invalid base64 image payload: {e}

Error message

Invalid base64 image payload: {e}

What it means

Raised in /v1/images/harmonize of scripts/mlx_image_server.py with HTTP 400 when base64.b64decode fails on the request's image (or mask) payload. The handler strips an optional data-URL prefix with split(',', 1)[-1] then decodes; any binascii.Error (bad padding, non-alphabet chars) or TypeError is chained into the 400 with its message.

Source

Thrown at scripts/mlx_image_server.py:429

                out_images.append({"b64_json": base64.b64encode(out_path.read_bytes()).decode("ascii")})
        return {"created": 0, "data": out_images}
    raise HTTPException(
        422,
        "This MLX image endpoint supports text-to-image generation only. "
        "Use /v1/images/generations, or serve an edit/img2img-capable model.",
    )


@app.post("/v1/images/harmonize")
def harmonize_image(req: HarmonizeRequest):
    active_model = _args.model
    if _is_lama_inpaint(active_model) or _is_ddcolor(active_model):
        try:
            image_raw = base64.b64decode(req.image.split(",", 1)[-1])
            mask_b64 = req.body_mask or req.mask
            mask_raw = base64.b64decode(mask_b64.split(",", 1)[-1]) if mask_b64 else None
        except Exception as e:
            raise HTTPException(400, f"Invalid base64 image payload: {e}") from e
        with tempfile.TemporaryDirectory(prefix="odysseus-mlx-harmonize-") as td:
            out_path = Path(td) / "image.png"
            if _is_ddcolor(active_model):
                _run_ddcolor_bridge(active_model, image_raw, out_path)
            else:
                _run_inpaint_bridge(active_model, image_raw, mask_raw, out_path)
            if not out_path.exists():
                raise HTTPException(500, f"MLX Swift bridge completed but did not write {out_path}")
            return {"image": base64.b64encode(out_path.read_bytes()).decode("ascii")}
    raise HTTPException(
        422,
        "This MLX image endpoint supports text-to-image generation only. "
        "Use /v1/images/generations, or serve an edit/img2img-capable model.",
    )


def main() -> None:
    global _args

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Validate client-side before sending: base64.b64decode(payload.split(',')[-1], validate=True) round-trips
  2. Send a proper data URL or pure base64 string of the PNG, no newlines or URL-encoding
  3. If sending a mask, apply the same checks to body_mask/mask
  4. Log the payload length/prefix on failure to spot URL-vs-base64 mistakes

Example fix

# before
{"image": "https://cdn/img.png"}  # 400 Invalid base64
# after
import base64
{"image": "data:image/png;base64," + base64.b64encode(open('img.png','rb').read()).decode()}
Defensive patterns

Strategy: validation

Validate before calling

import base64, binascii

def to_data_url(png_path: str) -> str:
    b64 = base64.b64encode(open(png_path, 'rb').read()).decode('ascii')
    base64.b64decode(b64, validate=True)  # self-check
    return 'data:image/png;base64,' + b64

Type guard

def is_valid_b64_payload(s: str) -> bool:
    try:
        base64.b64decode(s.split(',', 1)[-1], validate=True)
        return True
    except Exception:
        return False

Try / catch

try:
    post_harmonize(image=payload)
except HTTPException as e:
    if e.status_code == 400 and 'Invalid base64' in str(e.detail):
        payload = encode_file_properly(png_path); retry_once()
    else:
        raise

Prevention

When it happens

Trigger: POST /v1/images/harmonize with req.image that is not valid base64: plain URL to an image, raw binary bytes JSON-encoded incorrectly, base64 with newlines/spaces from terminal copy-paste, or a data URL whose payload after the comma was truncated; same for body_mask/mask fields.

Common situations: Frontend sends encodeURIComponent'd raw binary instead of base64; copy-paste from 'base64 -w0' vs wrapped output; data URI missing its comma so the whole 'data:image/png;base64,...' string is decoded; trailing '=' padding stripped by a transport layer.

Related errors


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