fix(image-gen): unbreak Codex-auth image generation and stop blaming the user's account - #71635
Merged
Merged
Conversation
…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.
Contributor
૮ >ﻌ< ა ci reviewran on b6b7ff2 all good! |
Contributor
This was referenced Jul 25, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
tool_choiceblock alongside the hostedimage_generationtool. The Codex backend resolvestool_choiceas a function-tool name and never recognizes hosted-tool entries, so it returnedHTTP 400: Tool choice 'image_generation' not found in 'tools' parameter.on every request, for every account.Changes
plugins/image_gen/openai-codex/__init__.pytool_choiceblock from_build_responses_payload()(@Tranquil-Flow's commit) —instructionsdo the steering.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._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 parsederror.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
tool_choiceon the wireHTTP 400: Tool choice 'image_generation' not found in 'tools' parameter.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 notool_choiceon the wire andoriginator: codex_cli_rs; the 400 path returnsapi_errorcarrying 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 reportsexperimental_supported_tools: []on the Codex models endpoint; @CrazyBoyM reports a live success with aninput_imagepart. 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_choiceremoval 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 asimplemented_on_mainpointing 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