{"record":{"id":"8b27f801d70353f4","repo":"odysseus-dev/odysseus","slug":"invalid-base64-image-payload-e","errorCode":null,"errorMessage":"Invalid base64 image payload: {e}","messagePattern":"Invalid base64 image payload: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"scripts/mlx_image_server.py","lineNumber":429,"sourceCode":"                out_images.append({\"b64_json\": base64.b64encode(out_path.read_bytes()).decode(\"ascii\")})\n        return {\"created\": 0, \"data\": out_images}\n    raise HTTPException(\n        422,\n        \"This MLX image endpoint supports text-to-image generation only. \"\n        \"Use /v1/images/generations, or serve an edit/img2img-capable model.\",\n    )\n\n\n@app.post(\"/v1/images/harmonize\")\ndef harmonize_image(req: HarmonizeRequest):\n    active_model = _args.model\n    if _is_lama_inpaint(active_model) or _is_ddcolor(active_model):\n        try:\n            image_raw = base64.b64decode(req.image.split(\",\", 1)[-1])\n            mask_b64 = req.body_mask or req.mask\n            mask_raw = base64.b64decode(mask_b64.split(\",\", 1)[-1]) if mask_b64 else None\n        except Exception as e:\n            raise HTTPException(400, f\"Invalid base64 image payload: {e}\") from e\n        with tempfile.TemporaryDirectory(prefix=\"odysseus-mlx-harmonize-\") as td:\n            out_path = Path(td) / \"image.png\"\n            if _is_ddcolor(active_model):\n                _run_ddcolor_bridge(active_model, image_raw, out_path)\n            else:\n                _run_inpaint_bridge(active_model, image_raw, mask_raw, out_path)\n            if not out_path.exists():\n                raise HTTPException(500, f\"MLX Swift bridge completed but did not write {out_path}\")\n            return {\"image\": base64.b64encode(out_path.read_bytes()).decode(\"ascii\")}\n    raise HTTPException(\n        422,\n        \"This MLX image endpoint supports text-to-image generation only. \"\n        \"Use /v1/images/generations, or serve an edit/img2img-capable model.\",\n    )\n\n\ndef main() -> None:\n    global _args","sourceCodeStart":411,"sourceCodeEnd":447,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/scripts/mlx_image_server.py#L411-L447","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Validate client-side before sending: base64.b64decode(payload.split(',')[-1], validate=True) round-trips","Send a proper data URL or pure base64 string of the PNG, no newlines or URL-encoding","If sending a mask, apply the same checks to body_mask/mask","Log the payload length/prefix on failure to spot URL-vs-base64 mistakes"],"exampleFix":"# before\n{\"image\": \"https://cdn/img.png\"}  # 400 Invalid base64\n# after\nimport base64\n{\"image\": \"data:image/png;base64,\" + base64.b64encode(open('img.png','rb').read()).decode()}","handlingStrategy":"validation","validationCode":"import base64, binascii\n\ndef to_data_url(png_path: str) -> str:\n    b64 = base64.b64encode(open(png_path, 'rb').read()).decode('ascii')\n    base64.b64decode(b64, validate=True)  # self-check\n    return 'data:image/png;base64,' + b64","typeGuard":"def is_valid_b64_payload(s: str) -> bool:\n    try:\n        base64.b64decode(s.split(',', 1)[-1], validate=True)\n        return True\n    except Exception:\n        return False","tryCatchPattern":"try:\n    post_harmonize(image=payload)\nexcept HTTPException as e:\n    if e.status_code == 400 and 'Invalid base64' in str(e.detail):\n        payload = encode_file_properly(png_path); retry_once()\n    else:\n        raise","preventionTips":["Always build payloads with base64.b64encode, never hand-copied strings","Strip data-URL prefixes yourself and log payload length on error","Never URL-encode or wrap base64 across newlines in JSON"],"tags":["base64","validation","http-400","harmonize"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}