Skip to content

fix(codex): raise max_output_tokens below the Codex minimum of 16 - #7288

Closed
RyoKusnadi wants to merge 2 commits into
router-for-me:devfrom
RyoKusnadi:fix/codex-min-max-output-tokens
Closed

RyoKusnadi wants to merge 2 commits into
router-for-me:devfrom
RyoKusnadi:fix/codex-min-max-output-tokens

Conversation

@RyoKusnadi

@RyoKusnadi RyoKusnadi commented Nov 6, 2026 •

Copy link
Copy Markdown

Problem

In Claude Code, /model gpt-5.6-sol fails with 400 Invalid 'max_output_tokens': integer below minimum value. Expected a value >= 16, but got 1 instead, and the model is not changed. It worked on v8.0.51.

How it happens

Claude Code checks a model with a probe request that has max_tokens: 1. Since 01b24f1 (#7230) the Codex request translators forward the client's token limit as max_output_tokens, so the 1 reaches the Codex Responses API, which rejects anything below 16. The OpenAI chat, Gemini, Interactions and Responses translators forward it the same way.

Steps to reproduce

  1. Run CLIProxyAPI with a Codex credential.
  2. POST /v1/messages with {"model":"gpt-5.6-sol","max_tokens":1,"messages":[{"role":"user","content":"Hi"}]}.

Expected: the request goes through. Actual: upstream 400 as above.

Cause

internal/translator/codex/* copy the client limit to max_output_tokens unchanged. Codex has a minimum of 16 that the translators don't know about.

Fix

A new clampCodexMaxOutputTokensDoc raises a max_output_tokens below 16 to 16 on the request document. Each Codex executor path (HTTP execute, stream, compact, websocket execute and stream) calls it right after ensureWebSearchToolForHistoryDoc, on the same *ojson.Doc, before the payload finalizer, so user payload rules still apply last. It lives in the executor because AGENTS.md asks for no standalone internal/translator/ changes, and a per-translator fix would need four copies.

Scope

Larger values, a missing field and null are untouched. #7249 also hits this minimum, from credential verification; with this change that path sends 16 as well. I updated the existing verification test, which expected the 1 to be kept. Other providers (xAI, Claude) are not changed.

Testing

New tests in codex_executor_translate_test.go: TestClampCodexMaxOutputTokensDoc (1, 0, 15, 16, 4096, absent, null) and TestClampCodexMaxOutputTokensDocClaudeProbe (real Claude to Codex translator gives 1, the clamp makes it 16). The existing TestCodexVerifyInferenceTranslatesPayloadAndRequiresCompletedEvent_Issue5786 covers the executor end to end: before the fix the upstream body has max_output_tokens: 1, after it 16.
Ran go test ./internal/runtime/executor/ (4735 passed), go vet on the executor and go build ./.... Not run: the full suite and a live Codex request.

Environment: fix/codex-min-max-output-tokens @ 471f086 (on dev), go1.27.1, darwin/arm64

Fixes #7282

Signed-off-by: Ryo Kusnadi <ryokusnadi@gmail.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-06T02:13:14.610827Z 471f086 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fcce8efb07

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

if errSet := doc.Set("max_output_tokens", codexMinMaxOutputTokens); errSet != nil {
return body
}
return doc.Bytes()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the clamp in the existing document mutation chain

Whenever a sub-16 value triggers this path, doc.Bytes() serializes the request at this helper boundary, but every production caller then reparses body into another ojson.Doc (for example Execute in codex_executor_execute.go:60 and WebSocket preparation in codex_websockets_stream.go:893) before applying the remaining normalization and finalization stages. Move this clamp onto those existing request documents before payload finalization, or pass the document onward, so the hot request path retains the required single-document mutation/serialization pipeline.

AGENTS.md reference: AGENTS.md:L53-L53

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. Moved it to clampCodexMaxOutputTokensDoc(doc), called on the existing request document in each Codex executor path right after ensureWebSearchToolForHistoryDoc, so there's no extra serialize/reparse. Done in 471f086, description updated.

Signed-off-by: Ryo Kusnadi <ryokusnadi@gmail.com>
@RyoKusnadi RyoKusnadi closed this Nov 6, 2026
@RyoKusnadi
RyoKusnadi deleted the fix/codex-min-max-output-tokens branch November 6, 2026 13:18
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