odysseus-dev/odysseus · error · HTTPException
OpenAI edit failed
Error message
OpenAI edit failed
What it means
Raised inside a FastAPI proxy route when the upstream OpenAI-compatible /v1/images/edits endpoint answers with a non-200 status. The route forwards the upstream status code verbatim as the HTTPException status, with the generic detail 'OpenAI edit failed'. It means the multipart edit request reached the server but was rejected or failed server-side.
Source
Thrown at routes/gallery/gallery_routes.py:1380
}
# Honor explicit model selection from the editor; fall back to gpt-image-1.
# dall-e-3 has no edit endpoint — refuse it loudly so the user picks again.
oa_model = chosen_model or "gpt-image-1"
if "dall-e-3" in oa_model:
raise HTTPException(400, "dall-e-3 doesn't support image edits — pick gpt-image-1 or dall-e-2")
data = {
"model": oa_model,
"prompt": body.get("prompt", ""),
"size": size,
"n": "1",
}
headers = {"Authorization": f"Bearer {api_key}"}
try:
async with httpx.AsyncClient(timeout=120) as client:
r = await client.post(_join_checked_gallery_endpoint(base, "/images/edits"), headers=headers, data=data, files=files)
if r.status_code != 200:
logger.error("inpaint_proxy OpenAI edit: status %s", r.status_code)
raise HTTPException(r.status_code, "OpenAI edit failed")
result = r.json()
raw_b64 = None
if result.get("data"):
item = result["data"][0]
# gpt-image-1 returns b64_json by default; dall-e-2 may return url
if item.get("b64_json"):
raw_b64 = item["b64_json"]
elif item.get("url"):
raw_b64 = await _fetch_result_image_b64(item["url"])
if not raw_b64:
raise HTTPException(502, "OpenAI returned no image")
# OpenAI's edits API doesn't truly preserve unmasked
# pixels — gpt-image-1 regenerates the whole image,
# so even areas the user didn't mask come back
# slightly different. Composite the model output onto
# the ORIGINAL source using the user's mask, so only
# the masked region actually changes.View on GitHub (pinned to f9235ebbf1)
Solutions
- Check the server log line 'inpaint_proxy OpenAI edit: status %s' to get the exact upstream code, then fix accordingly (401 → rotate API key, 429 → quota, 400 → payload/model).
- Re-verify the configured image endpoint's base_url and api_key in the gallery endpoint settings (must be a valid OpenAI-compatible base ending before /v1).
- Confirm the model chosen (oa_model) is one the upstream account can use for image edits.
- If upstream is self-hosted (not OpenAI), ensure it actually implements /v1/images/edits or let the route take the diffusion-server path instead.
Example fix
// before
if r.status_code != 200:
logger.error("inpaint_proxy OpenAI edit: status %s", r.status_code)
raise HTTPException(r.status_code, "OpenAI edit failed")
// after — surface the upstream detail so the client sees the real cause
if r.status_code != 200:
detail = "OpenAI edit failed"
try:
detail = r.json().get("error", {}).get("message", detail)
except Exception:
pass
logger.error("inpaint_proxy OpenAI edit: status %s %s", r.status_code, detail)
raise HTTPException(r.status_code, detail) Defensive patterns
Strategy: try-catch
Validate before calling
import httpx
# Smoke-test credentials/model before launching the proxy call
async def endpoint_ready(base: str, api_key: str, model: str) -> bool:
try:
async with httpx.AsyncClient(timeout=15) as c:
r = await c.get(f"{base}/models", headers={"Authorization": f"Bearer {api_key}"})
if r.status_code != 200:
return False
return any(m.get("id") == model for m in r.json().get("data", []))
except httpx.HTTPError:
return False Try / catch
try:
resp = await client.post("/api/image/inpaint", json=payload)
resp.raise_for_status()
except httpx.HTTPStatusError as e:
detail = e.response.json().get("detail", "")
if "OpenAI edit failed" in str(detail):
# inspect server log for the upstream status; 401/429 need user action
show_error(f"Upstream image edit rejected ({e.response.status_code}): {detail}")
else:
raise Prevention
- Keep the image endpoint's api_key and base_url current in the endpoint settings.
- Log and surface upstream error bodies instead of the generic message when debugging.
- Prefer b64_json response_format so no second URL fetch is needed.
When it happens
Trigger: POST to the gallery inpaint proxy (inpaint_proxy) that takes the OpenAI path: a valid api_key endpoint base ending in /v1, image+mask files assembled, and the POST to {base}/images/edits returning e.g. 401 (bad key), 400 (malformed image/mask or bad model name), 429 (rate limit), or 5xx.
Common situations: Expired or revoked OpenAI API key stored in the endpoint DB row; model name not available to the account (e.g. gpt-image-1 not entitled); mask PNG not the same dimensions as the image; quota exhaustion returning 429; pointing the endpoint at a non-OpenAI server that returns errors on /images/edits.
Related errors
- Image edit request failed
- Session request returned an invalid response
- GitHub device-code request failed (HTTP {status})
- GitHub device-code request failed: {e}
- Login succeeded but provisioning failed: {e}
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/54e472b21773e76a.
Report an issue: GitHub.