Client or integration
Claude Code (Anthropic Messages ingress), with a failover combo.
Provider or upstream service
Canonical ChatGPT Codex forward destination (openai-responses, authMode: forward). Observed model: gpt-5.3-codex-spark. The user sanitation defect is destination-scoped, not a request to restore any retired model-specific exception.
OpenCodex version
Observed in installed @bitkyc08/opencodex 2.52.0 on Linux on 2026-09-13. The same missing sanitation and terminal classification were reproduced against current dev base df7dc1b (package development version 2.54.0) with synthetic regression tests.
Endpoint or capability
POST /v1/messages → Responses translation → canonical Codex forward wire; pre-output combo failover after an HTTP 400 optional-control incompatibility.
Current behaviour
src/claude/inbound.ts maps metadata.user_id to top-level Responses user and separately derives prompt_cache_key.
src/adapters/openai-responses.ts removes other unsupported native-forward fields, but leaves user on the canonical ChatGPT wire.
- Actual ordered attempt telemetry showed Anthropic returning 429, followed by the native Codex target returning
400 / invalid_request_error / Unsupported parameter: user. The combo ended at that target despite subsequent configured targets.
- Separate requests to that native target also returned
unsupported_value for reasoning.effort: none, with supported values listed as low/medium/high/xhigh. The combo's generic invalid_request_error stop similarly prevented fallback. A configured default of low does not replace an explicit inbound none.
No credentials, account identifiers, conversation bodies, or private project files are included here.
Expected behaviour
- Remove top-level
user only at the canonical Codex destination. Keep the original translated/replay body, session/cache identity, input roles, tool-schema properties and safety identifiers unchanged. Public/key-auth and noncanonical forward destinations must not inherit this deletion.
- A bounded, intact error envelope specifically rejecting optional
user, or reporting unsupported_value for the reasoning-effort field, may advance to the next eligible unattempted combo target before output commitment.
- These are request-local incompatibilities, not evidence of an unhealthy account/provider: do not record a target/provider cooldown for them.
- Do not turn every HTTP 400 into a retry, silently drop reasoning controls, change
none to a more expensive level, retry the same attempted target, or replay after output/tool commitment. Cancellation, origin/cyber-policy refusals and ambiguous post-send errors remain terminal.
Minimal redacted request or reproduction
Synthetic Claude-shaped input, with a configured failover combo and a canonical Codex target:
{
"model": "combo/repro",
"max_tokens": 32,
"metadata": { "user_id": "synthetic-claude-session" },
"messages": [{ "role": "user", "content": "Reply OK." }]
}
For deterministic reproduction, use three synthetic upstream targets: A returns HTTP 429, B (openai-responses) returns either exact 400 envelope below, and C returns a valid completion. Exercise both streaming and non-streaming requests. Before the change, all four scenarios terminate with 400; with the proposed change, each reaches C exactly once and returns 200.
Actual response or error
{"error":{"type":"invalid_request_error","message":"Unsupported parameter: user"}}
The separately observed reasoning rejection:
{"error":{"type":"invalid_request_error","code":"unsupported_value","message":"Unsupported value: 'none' is not supported with the 'gpt-5.3-codex-spark' model. Supported values are: 'low', 'medium', 'high', and 'xhigh'.","param":"reasoning.effort"}}
Upstream documentation
The expected client requirement is concrete: Claude Code supplies metadata.user_id, and this repository's translator forwards it as user. I am not treating the public Responses API schema as a complete specification of the private Codex backend's accepted fields. The rejection above was observed in real attempt telemetry; the proposed destination guard reuses the repository's existing isCanonicalOpenAiForwardProvider authority. Related destination-envelope precedent: #2765 and #2668.
Suggested mapping or implementation notes
An isolated patch and regression tests are prepared for a dev-targeted pull request. The optional-control classifier accepts only the two narrow structured shapes, with a 16,384-character / three-envelope bound and exact OpenCodex wrapper recognition. Conflicting/unknown codes, reflected JSON, malformed/truncated envelopes and unrelated input/tool/safety fields do not acquire hop permission.
Verification so far: 214 focused tests passed, 0 failed, including the complete combo-server E2E file, the canonical-envelope suite, classification/security negatives, and existing reasoning-downgrade coverage. The final regressions were driven red against unchanged base source first: 4 canonical-envelope failures, 4 classification failures, and 4 synthetic E2E failures. Typecheck, privacy scan and structure checks passed. Full-suite/release-readiness verification is still in progress and will be reported with the PR's exact head.
Additional context and attachments
Please consider the reviewed fix for the next appropriate update. This report does not claim a deployed fix, an upstream merge, live-provider E2E of the patch, or a guaranteed release date.
Checks
Client or integration
Claude Code (Anthropic Messages ingress), with a failover combo.
Provider or upstream service
Canonical ChatGPT Codex forward destination (
openai-responses,authMode: forward). Observed model:gpt-5.3-codex-spark. Theusersanitation defect is destination-scoped, not a request to restore any retired model-specific exception.OpenCodex version
Observed in installed
@bitkyc08/opencodex2.52.0 on Linux on 2026-09-13. The same missing sanitation and terminal classification were reproduced against currentdevbase df7dc1b (package development version 2.54.0) with synthetic regression tests.Endpoint or capability
POST /v1/messages→ Responses translation → canonical Codex forward wire; pre-output combo failover after an HTTP 400 optional-control incompatibility.Current behaviour
src/claude/inbound.tsmapsmetadata.user_idto top-level Responsesuserand separately derivesprompt_cache_key.src/adapters/openai-responses.tsremoves other unsupported native-forward fields, but leavesuseron the canonical ChatGPT wire.400 / invalid_request_error / Unsupported parameter: user. The combo ended at that target despite subsequent configured targets.unsupported_valueforreasoning.effort: none, with supported values listed as low/medium/high/xhigh. The combo's genericinvalid_request_errorstop similarly prevented fallback. A configured default oflowdoes not replace an explicit inboundnone.No credentials, account identifiers, conversation bodies, or private project files are included here.
Expected behaviour
useronly at the canonical Codex destination. Keep the original translated/replay body, session/cache identity, input roles, tool-schema properties and safety identifiers unchanged. Public/key-auth and noncanonical forward destinations must not inherit this deletion.user, or reportingunsupported_valuefor the reasoning-effort field, may advance to the next eligible unattempted combo target before output commitment.noneto a more expensive level, retry the same attempted target, or replay after output/tool commitment. Cancellation, origin/cyber-policy refusals and ambiguous post-send errors remain terminal.Minimal redacted request or reproduction
Synthetic Claude-shaped input, with a configured failover combo and a canonical Codex target:
{ "model": "combo/repro", "max_tokens": 32, "metadata": { "user_id": "synthetic-claude-session" }, "messages": [{ "role": "user", "content": "Reply OK." }] }For deterministic reproduction, use three synthetic upstream targets: A returns HTTP 429, B (
openai-responses) returns either exact 400 envelope below, and C returns a valid completion. Exercise both streaming and non-streaming requests. Before the change, all four scenarios terminate with 400; with the proposed change, each reaches C exactly once and returns 200.Actual response or error
{"error":{"type":"invalid_request_error","message":"Unsupported parameter: user"}}The separately observed reasoning rejection:
{"error":{"type":"invalid_request_error","code":"unsupported_value","message":"Unsupported value: 'none' is not supported with the 'gpt-5.3-codex-spark' model. Supported values are: 'low', 'medium', 'high', and 'xhigh'.","param":"reasoning.effort"}}Upstream documentation
The expected client requirement is concrete: Claude Code supplies
metadata.user_id, and this repository's translator forwards it asuser. I am not treating the public Responses API schema as a complete specification of the private Codex backend's accepted fields. The rejection above was observed in real attempt telemetry; the proposed destination guard reuses the repository's existingisCanonicalOpenAiForwardProviderauthority. Related destination-envelope precedent: #2765 and #2668.Suggested mapping or implementation notes
An isolated patch and regression tests are prepared for a
dev-targeted pull request. The optional-control classifier accepts only the two narrow structured shapes, with a 16,384-character / three-envelope bound and exact OpenCodex wrapper recognition. Conflicting/unknown codes, reflected JSON, malformed/truncated envelopes and unrelated input/tool/safety fields do not acquire hop permission.Verification so far: 214 focused tests passed, 0 failed, including the complete combo-server E2E file, the canonical-envelope suite, classification/security negatives, and existing reasoning-downgrade coverage. The final regressions were driven red against unchanged base source first: 4 canonical-envelope failures, 4 classification failures, and 4 synthetic E2E failures. Typecheck, privacy scan and structure checks passed. Full-suite/release-readiness verification is still in progress and will be reported with the PR's exact head.
Additional context and attachments
Please consider the reviewed fix for the next appropriate update. This report does not claim a deployed fix, an upstream merge, live-provider E2E of the patch, or a guaranteed release date.
Checks