Skip to content

fix(image-gen): unbreak Codex-auth image generation and stop blaming the user's account - #71635

Merged
teknium1 merged 2 commits into
mainfrom
hermes/hermes-acb79477
Jul 25, 2026
Merged

teknium1 merged 2 commits into
mainfrom
hermes/hermes-acb79477

Conversation

@teknium1

Copy link
Copy Markdown
Collaborator

Summary

Image generation over ChatGPT/Codex OAuth was broken for every account, and the error message told users it was their account's fault — which is why this surfaced as "setting it up doesn't work" instead of a bug report.

Two defects, one user-visible symptom:

  1. Request-shape bug. The plugin sent a tool_choice block alongside the hosted image_generation tool. The Codex backend resolves tool_choice as a function-tool name and never recognizes hosted-tool entries, so it returned HTTP 400: Tool choice 'image_generation' not found in 'tools' parameter. on every request, for every account.
  2. The misdiagnosis that hid it. A classifier matched that exact 400 and replaced it with "Image generation is not enabled for the current Codex account. Switch the image provider to OpenAI API key, FAL, or xAI." A universal bug in our own payload was reported as the user's entitlement problem, sending people away from a provider that had never actually been tried.

Changes

  • plugins/image_gen/openai-codex/__init__.py
    • Drop the tool_choice block from _build_responses_payload() (@Tranquil-Flow's commit) — instructions do the steering.
    • Remove CodexImageGenerationUnsupportedError, _IMAGE_GENERATION_UNAVAILABLE_MESSAGE, and _is_image_generation_unsupported_error(). HTTP failures now surface verbatim. A comment marks the removal so the classifier isn't reintroduced.
    • Add _summarize_error_body(). Error bodies were head-truncated at 500 chars, and Codex error payloads can carry hundreds of bytes of leading metadata — users got a wall of padding and no message. Now prefers the parsed error.message, falls back to a truncated raw body.
  • tests/plugins/image_gen/test_openai_codex_provider.py — 4 regression tests; the misclassification test drives the real httpx boundary rather than mocking _collect_image_b64 (mocking it would skip the code under test).
  • website/docs/user-guide/features/image-generation.md — drop the unqualified image-to-image claim for this backend; document that the hosted tool call cannot be forced, so it is best-effort.

Validation

Before After
tool_choice on the wire sent → HTTP 400 every request omitted → request accepted
User-facing error "not enabled for the current Codex account" HTTP 400: Tool choice 'image_generation' not found in 'tools' parameter.
Error body 500 chars, mostly metadata padding 148 chars, actual message
Tests 189/189 pass in tests/plugins/image_gen/

Sabotage run: restoring the old behavior fails all 4 new tests (test_codex_stream_request_shape, test_tool_choice_400_surfaces_verbatim_not_as_capability_error, test_payload_omits_tool_choice, test_http_error_body_is_truncated_but_preserved), so they genuinely pin the fix.

E2E against a local fake Codex backend with real imports and a temp HERMES_HOME: success path writes a real PNG to the cache with no tool_choice on the wire and originator: codex_cli_rs; the 400 path returns api_error carrying the actionable wire message.

Scope — what this does NOT claim

This fixes the request-shape rejection and the misleading error. It does not prove end-to-end image generation works on every account. Two reporters observed that after the 400 is gone the host model may never emit an image_generation_call (→ empty_response), and #49008 reports experimental_supported_tools: [] on the Codex models endpoint; @CrazyBoyM reports a live success with an input_image part. Settling that needs a live Codex credential — every token on this box is expired. The docs note now sets the best-effort expectation instead of over-promising, and a real failure is finally legible when it happens.

Credit

tool_choice removal cherry-picked from #19979 by @Tranquil-Flow (first submitter, with the test), authorship preserved in git history. Same fix was independently submitted in #49161 (@kyssta-exe). Note #49054, #49065 and #50579 were closed as implemented_on_main pointing at the classifier commit — that classifier is what this PR removes, so those closures resolved the symptom's message rather than the bug.

Refs #19505, #49008, #31335.

Infographic

the-error-that-lied

Tranquil-Flow and others added 2 commits July 25, 2026 16:11
…load (#19505)

The chatgpt.com/backend-api/codex backend 400s on every tool_choice
shape for the hosted image_generation tool — it looks up tool_choice as
a function name and never recognizes hosted-tool entries. Removing the
field from _build_responses_payload() lets the host model decide; the
instructions field nudges it toward the tool.

Salvaged from PR #19979 (originally targeted the old
client.responses.stream call, which no longer exists on upstream/main;
the live request now flows through _build_responses_payload + httpx in
_collect_image_b64).
…t limit

The Codex image backend rejected our own request shape for every account, and
we then translated that rejection into "Image generation is not enabled for the
current Codex account. Switch the image provider to OpenAI API key, FAL, or
xAI." — telling every affected user to abandon a provider that had never
actually been tried. That message is why this reads as a setup failure rather
than a bug: the wire error was replaced with a confident, wrong diagnosis.

Removes the classifier and its exception, so any HTTP failure surfaces
verbatim. The paired request-shape fix (previous commit) is what makes the
400 stop happening; this commit makes the next one diagnosable.

Also fixes error-body truncation: bodies were head-truncated at 500 chars, and
Codex error payloads can carry hundreds of bytes of leading metadata, so the
user got a wall of padding and no message. _summarize_error_body() prefers the
parsed error.message and falls back to a truncated raw body.

Docs: drop the unqualified image-to-image claim for the Codex backend and note
that the hosted tool call cannot be forced, so it is best-effort.

Verified E2E against a local fake Codex backend: success path writes a real PNG
with no tool_choice on the wire; the 400 path now returns api_error carrying
"Tool choice 'image_generation' not found in 'tools' parameter" (148 chars)
instead of the entitlement message. Sabotage run confirms all 4 regression
tests fail when the old behavior is restored.

Refs #19505, #49008, #31335.
@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

૮ >ﻌ< ა ci review

ran on b6b7ff2

all good!

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/plugins Plugin system and bundled plugins tool/vision Vision analysis and image generation provider/openai OpenAI / Codex Responses API labels Jul 25, 2026
@alt-glitch

Copy link
Copy Markdown
Contributor

This was generated by AI during triage.

Supersedes the payload-only approach in #19979 for this path: this core-team PR also removes the misleading entitlement classifier and adds boundary-level regression coverage. Related to #49008.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have provider/openai OpenAI / Codex Responses API tool/vision Vision analysis and image generation type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants