Skip to content

fix(server): relay /v1/images to an OpenAI upstream for built-in image_gen - #87

Merged
Ingwannu merged 1 commit into
devfrom
fix/issue-83-images-endpoint
Jul 10, 2026
Merged

fix(server): relay /v1/images to an OpenAI upstream for built-in image_gen#87
Ingwannu merged 1 commit into
devfrom
fix/issue-83-images-endpoint

Conversation

@Ingwannu

Copy link
Copy Markdown
Owner

Closes #83.

Problem

Codex's built-in image_gen tool failed instantly with:

image generation failed: http 404 Not Found: Some("{\"error\":{\"message\":\"Unknown endpoint: POST /v1/images/generations\",...}")

The 404 is opencodex's own /v1/* data-plane guard. codex-rs's standalone image-generation extension executes client-side: it POSTs {base_url}/images/generations (/images/edits when reference images are attached) with the same ChatGPT bearer auth it uses for chat. Under Design B injection base_url is the proxy, and no route existed — the path was even test-pinned as an intentional 404.

Why reports started now: codex 0.144.0 graduated image_generation from the disabled-by-default imagegenext experiment to stable / default-enabled, so ChatGPT-signed-in users hit the tool organically. (The hosted image_generation entry in tools[] on /v1/responses was never affected — passthrough already forwards it; only the extension's direct REST call 404'd.)

Fix

New relay route POST /v1/images/{generations,edits} (src/server/images.ts), inserted before the /v1/* guard with the standard data-plane preamble (drain 503, requireApiAuth, origin check, withCors, request log incl. client_cancel meta).

Upstream selection:

  1. ChatGPT forward provider (preferred — the backend codex itself would have called absent the base_url override, so bodies relay verbatim, zero mapping). Auth = forwarded caller bearer or the routed multi-account pool token (resolveCodexAuthContext / headersForCodexAuthContext); pool upstream outcomes are recorded for rotation failover.
    • Used only when the request actually carries a relayable bearer — startServer auto-upserts a chatgpt forward entry into every config, so its presence proves nothing; without this gate an unauthenticated request bounces off chatgpt.com as an opaque {"detail":"Unauthorized"}.
    • A bearer equal to the proxy's own admission secret (non-loopback binds accept Authorization: Bearer $OPENCODEX_API_AUTH_TOKEN) is stripped and never relayed to chatgpt.com.
    • Forward-auth failures (pool cooldown 429 / reauth 401 / affinity 409) fall back to the keyed provider instead of failing the request; the captured error surfaces only when no keyed candidate exists.
  2. Keyed openai-responses provider (e.g. api.openai.com) — URL normalized like the adapter (baseUrl with or without /v1 both work).
  3. Neither — honest 400 naming the fix (add an OpenAI provider, or codex features disable image_generation). 4xx, not 5xx, because codex retries every 5xx up to 5 total attempts and this is a permanent config state.

Relay: readJsonRequestBody (zstd/gzip + 256MB cap) → fetchWithResetRetry with config.images.timeoutMs (default 300s) linked to client abort → response passed through status+content-type+body verbatim so upstream plan-gating errors stay legible to codex (it Debug-prints the body into the model-visible failure). Client cancel → 499, upstream hang → 504.

Docs: endpoint lists + new "Built-in image generation" section in the codex-integration guide (en/ko/zh-cn) and architecture reference (en/ko/zh-cn). Design notes in devlog/260710_issue83_images_endpoint/.

Tests

  • 16 new tests in tests/server-images.test.ts: forward relay (auth/path/body), edits path, pool-token override (caller bearer must not leak), zstd decode, keyed fallback + /v1 normalization, unauthenticated-request gate, forward-auth-failure fallback, no-upstream 400, upstream error passthrough, 504 timeout, GET→404 guard, non-loopback auth/origin, admission-secret never relayed.
  • tests/server-auth.test.ts 404-guard list swaps /v1/images/generations for /v1/realtime/sessions.
  • Full suite: 1945 pass / 0 fail; tsc --noEmit clean.
  • Reviewed by a 4-lens adversarial multi-agent pass (correctness / security / codex-client compat vs rust-v0.144.0 source / project fit); all 13 confirmed findings addressed in this diff.

🤖 Generated with Claude Code

…e_gen (#83)

codex-rs's standalone image_gen extension POSTs {base_url}/images/generations
(and /images/edits) client-side with its ChatGPT bearer auth. Under Design B
injection base_url is the proxy, so the call died on the /v1/* JSON-404 guard:
"Unknown endpoint: POST /v1/images/generations". codex 0.144.0 made the tool
stable/default-on, so ChatGPT users now hit this organically.

Add POST /v1/images/{generations,edits} (src/server/images.ts), relaying the
body verbatim to the ChatGPT forward provider (caller bearer or routed pool
token; pool outcomes recorded) with fallback to a keyed openai-responses
provider, and an honest 400 when neither exists (4xx, not 5xx — codex retries
every 5xx up to 5 attempts). The forward candidate is only used when the
request carries a relayable bearer (startServer auto-upserts the chatgpt
provider, so presence proves nothing), forward-auth failures fall back to the
keyed provider before surfacing, and a bearer matching the proxy's own
admission secret is never relayed upstream. Client cancel maps to 499,
upstream hang to 504 after config.images.timeoutMs (default 300s).

Closes #83

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Ingwannu
Ingwannu merged commit 75d62e6 into dev Jul 10, 2026
6 checks passed
@lidge-jun
lidge-jun deleted the fix/issue-83-images-endpoint branch July 14, 2026 07:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant