Skip to content

[Provider]: xAI Responses opt-in (grok-4.x) 400s on codex_app automation_update — openai-responses path skips root-schema flattening #2753

Closed

Description

@bluesmilery

Client or integration

Codex App (Codex Desktop 0.150.0-alpha.8, which injects the built-in codex_app automation MCP toolset into every routed request)

Provider or upstream service

xai (api.x.ai, OAuth)

OpenCodex version

2.33.0 (@bitkyc08/opencodex)

Endpoint or capability

/v1/responses — the xAI native Responses opt-in (xaiResponsesOptIn), which flips providers.xai.modelAdapters for grok-4.6/grok-4.5 from openai-chat to openai-responses; affects client tool declarations / tool calls.

Current behaviour

An existing Codex Desktop thread runs normally while grok-4.6 routes through the provider-level openai-chat adapter: consecutive requests return 200 with the full tool list, including the built-in mcp__codex_app__* automation tools.

After the xAI Responses opt-in is enabled and routing switches to openai-responses, the very next sampling attempt of the same thread fails upstream with HTTP 400 in ~2.5 s. Every further turn fails identically because the same client tool list is redeclared on every request — the thread becomes unusable until the adapter is reverted.

Upstream rejects the Codex App automation_update tool, whose parameter schema root is an anyOf union containing a nested union (i.e. a non-object branch); minimized shape, same family as #122:

{
  "anyOf": [
    { "type": "object", "properties": { "...": { } } },
    { "oneOf": [ { "type": "object" }, { "type": "object" } ] }
  ]
}

Per xAI's function-calling docs, a root oneOf/anyOf is only accepted when every branch is directly an object schema; nested unions count as non-object branches.

Timeline from usage.jsonl (single proxy instance, same conversation ID, same tool catalog throughout — the only delta is the selected adapter):

10:30:21 UTC   adapter=openai-chat        -> 200
10:31:20 UTC   adapter=openai-chat        -> 200
               < xaiResponsesOptIn enabled; modelAdapters rewritten >
10:52:04 UTC   adapter=openai-responses   -> 400 invalid_request_error (first attempt, ~2.5 s)

Expected behaviour

Routing grok-4.6/grok-4.5 through the native Responses path should apply the same xAI-specific tool-schema sanitization the openai-chat adapter already performs (normalizeXaiToolParameters, introduced by #124 for #122): flatten the union root when the merge is lossless, and otherwise omit just the incompatible tool rather than forwarding a declaration xAI is guaranteed to reject. Today a single built-in Codex App tool poisons the entire request.

Minimal redacted request or reproduction

  1. Log in to xAI OAuth, pick xai/grok-4.6 in Codex Desktop under default openai-chat routing, confirm turns succeed.
  2. Enable the native Responses opt-in (dashboard toggle or management API):
curl -X PUT http://127.0.0.1:<port>/api/providers/<provider-config-route> \
  -H 'Authorization: Bearer <admin-token>' \
  -H 'Content-Type: application/json' \
  -d '{"xaiResponsesOptIn": true}'
  1. Continue the same Codex App thread (its requests carry the built-in codex_app automation tools).
  2. Continuation returns 400 invalid_client_tool_schema naming mcp__codex_app__automation_update, and every subsequent turn repeats it.

Adapter-dependence check: earlier in the very same thread, the identical tool list produced 200s while routed through openai-chat; switching routing to openai-responses reproduces the 400 deterministically.

Actual response or error

HTTP 400
{"code":"invalid-argument","error":"Failed to start sampling: [invalid_client_tool_schema] mcp__codex_app__automation_update: tool parameter root must be an object type (root schema is an anyOf/oneOf union with a non-object branch)"}

Proxy-side record: status 400, errorCode: invalid_request_error, adapter openai-responses, first attempt.

Upstream documentation

https://docs.x.ai/developers/tools/function-calling#parameter-schema — parameter schemas for function calling; root oneOf/anyOf accepted only when all branches are direct object schemas (the same requirement quoted in #122).

Suggested mapping or implementation notes

  • normalizeFunctionToolSchema on the responses path (src/adapters/openai-responses.ts, around line 505) only stamps "type": "object" next to existing keywords when the root is not an object. A root holding anyOf/oneOf still ships as a union, and when a branch is itself a union xAI rejects the whole request — the sibling type keyword does not neutralize the union for their validator.
  • For isXaiSchemaTarget providers, reuse/export the lossless root-flattening from normalizeXaiToolParameters (src/adapters/openai-chat.ts) in the responses translation step. Where flattening is not lossless, dropping just the offending tool (the chat adapter's behavior) keeps the thread alive — the other codex_app tools carry plain object roots.
  • Ideally make the opt-in/discovery layer aware of this so enabling xaiResponsesOptIn cannot silently regress previously-working threads.

Additional context and attachments

Checks

  • I searched existing provider and compatibility issues.
  • The request and response were redacted.
  • The expected behaviour is based on an upstream specification or a concrete client requirement.

Activity

  1. added
    account-poolOAuth, credentials, Codex pool, quota, failover, plans
    providerProvider adapters, OpenAI-compat presets, upstream API quirks
    proxyHTTP proxy, routing, reverse-proxy / management auth
    on Aug 27, 2026
  2. lidge-jun commented on Aug 27, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 42 / 80

    이 이슈는 Codex Desktop이 매 요청에 넣는 mcp__codex_app__automation_update 도구 스키마 때문에 생긴다. 그 도구의 parameters 루트가 anyOf이고, 그 안에 또 oneOf가 들어 있다. xAI 문서는 루트 anyOf/oneOf를 모든 가지가 바로 object일 때만 받는다. 중첩 유니온은 non-object branch로 취급되어 invalid_client_tool_schema 400이 난다.

    작성자가 적은 타임라인도 그 그림과 맞다. 같은 스레드·같은 도구 목록인데 openai-chat일 때는 200, xaiResponsesOptIn으로 openai-responses로 바꾼 뒤에는 바로 400이다. 예전 openai-chat 경로는 #122/#124에서 normalizeXaiToolParameters로 이미 고쳤고, Responses opt-in이 그 정규화를 우회하는 두 번째 입구였다는 설명도 맞다. 리포터 환경은 2.33.0이다.

    다만 지금 dev(HEAD eeb774026, package 2.34.0) 기준으로는 이미 고쳐져 있다. #2690(fix(xai): normalize Responses root tool schemas, 7a28fa552)이 src/adapters/openai-responses.ts의 normalizeFunctionToolSchema에서 xaiTarget일 때 normalizeXaiToolParameters를 그대로 호출한다. 지금 코드는 대략 이렇게 보인다.

    // src/adapters/openai-responses.ts ~L540
    function normalizeFunctionToolSchema(tool, xaiTarget) {
      if (xaiTarget) {
        const parameters = normalizeXaiToolParameters(...);
        return parameters === undefined ? undefined : { ...tool, parameters };
      }
      // non-xAI: type:"object" stamp only
    }
    

    즉 이슈 본문이 지적한 “Responses 경로는 type stamp만 한다”는 서술은 2.33.0에는 맞고, 현재 dev에는 더 이상 맞지 않는다. 평탄화에 실패하면 그 도구만 빼고(undefined → omit), tool_choice도 reconcileToolChoiceForOmittedTools로 맞춘다. automation_update처럼 중첩 유니온이 lossless merge가 안 되면 해당 도구만 빠지고 나머지 codex_app 도구는 살아남아 스레드가 죽지 않는 쪽이 기대 동작이다.

    라인 - (이슈 본문 제안) openai-responses.ts ~505 “type stamp만” — 현재 dev에서는 ~L540이 normalizeXaiToolParameters를 탄다. 제안 패치는 이미 #2690에 들어 있다.
    경로 - src/adapters/xai-tool-schema.ts normalizeXaiToolParameters / expandXaiRootObjectSchemas — 루트 유니온 평탄화·실패 시 omit이 여기 있다. 추가 구현이 아니라 회귀 확인 대상이다.
    경로 - 리포터 버전 2.33.0 vs dev 2.34.0 — main에 아직 안 올라간 dev-only 픽스일 수 있으니, “릴리스에 포함됐는지”만 확인하면 된다.

    메인테이너의 판단이 필요한 지점

    • 이슈를 이미 dev에 픽스됨(fix(xai): normalize Responses root tool schemas #2690) 으로 닫을지, 다음 정식 릴리스 태그에 포함될 때까지 open으로 둘지.
    • automation_update 중첩 anyOf/oneOf를 유닛 테스트로 한 번 더 핀할지(이미 유사 케이스가 있으면 중복 close).
    • account-pool 라벨은 이 버그와 직접 관련은 약해 보이지만, 라벨은 건드리지 않는다.

    너의 추천
    작성자에게 “2.33.0의 Responses 우회 버그는 맞다. 현재 dev는 #2690으로 같은 normalizeXaiToolParameters를 Responses 경로에도 넣었다. dev 또는 그 픽스가 들어간 다음 릴리스로 올린 뒤 같은 Codex App 스레드로 재현이 사라지는지 확인해 달라”고 안내하고, 재현이 안 되면 completed/superseded by #2690 으로 닫자. 새 코드 패치는 지금 dev 기준으로 필요 없다.

    이 댓글은 grok-bot이 작성했습니다

  3. Ingwannu commented on Aug 27, 2026

    @Ingwannu
    Owner

    Confirmed: this was a real 2.33.0 adapter-parity bug, but it is already fixed on dev by merged PR #2727, which carries the #2690 xAI Responses normalization change.

    On current dev at 8b1b65b8d858d3b92e4fd4852e9e368d43e73f39, the xAI branch in normalizeFunctionToolSchema() now delegates to normalizeXaiToolParameters(). An incompatible nested-union tool such as mcp__codex_app__automation_update is omitted instead of poisoning the whole request, and tool_choice is reconciled when an omitted tool was referenced.

    I re-ran the focused xAI Responses/schema/transport suites in an isolated HOME on that exact dev head: 163 passed, 0 failed. No duplicate patch is needed. I am closing this as completed on dev; users on 2.33.0 need a release containing #2727. If the same 400 persists after upgrading to that release, please reopen with the redacted tool schema and adapter record.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    account-poolOAuth, credentials, Codex pool, quota, failover, plansproviderProvider adapters, OpenAI-compat presets, upstream API quirksprovider-compatibilityProvider compatibility reportsproxyHTTP proxy, routing, reverse-proxy / management auth

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions