Skip to content

[Provider compatibility] xAI Responses: interrupted tool-call history snowballs because adjacency repair is not seeded #4870

Description

@MerryEcho

Client or integration

Codex App

Provider or upstream service

xai

OpenCodex version

2.57.0

Endpoint or capability

/v1/responses tool history replay

Current behaviour

On Codex Desktop through OpenCodex, xai/grok-4.6 OAuth uses the native Responses wire (modelWireDefaults, xaiResponsesDefaultVersion: 1). The first failed tool turn in a thread is often an upstream 502 / connection-reset (commonly around 15s). After that, later turns in the same thread fail much more often, while neighboring healthy threads on the same machine keep returning 200.

Local usage log (redacted counts, OpenCodex 2.57.0):

  • google-antigravity: about 1.06% failed requests
  • xAI grok-4.6: about 3.3-3.9% failed requests, including 200 HTTP 502 and 150 connection-reset events, with a cluster near 15s
  • After the first failure in a thread, the next 10 requests in that thread fail about 20.6% of the time
  • Threads that never fail stay clean (one sampled healthy thread: 37/37 and 4529/4529 successes)

Google already synthesizes a missing tool_result ([missing tool_result for this tool_use in history], #2199). xAI Responses does not. Codex can therefore replay a function_call / custom_tool_call with no matching output, or with hook-injected developer context between the pair. The existing repair (normalizeResponsesToolResultAdjacency + orphan-call placeholder) is gated on requiresAdjacentResponsesToolResults / statelessResponses. DeepSeek and Kimi seed that capability; xAI does not. xAI also cannot be marked statelessResponses: the public Responses API stores conversations for 30 days and documents previous_response_id.

This is distinct from the 15s upstream reset itself. Adjacency repair cannot stop the first 502; it stops the dirty history from 400ing every later turn.

Expected behaviour

xAI should get the same Responses tool-history repair as Kimi (#4726) and DeepSeek (#1292): seed requiresAdjacentResponsesToolResults: true, reorder an unambiguous call/result batch around hook-injected context, and synthesize an honest placeholder function_call_output when a call has no recorded result. Do not set statelessResponses (would strip store / previous_response_id). Forward-auth replay should stay fail-closed and not synthesize.

Minimal redacted request or reproduction

Codex App -> local OpenCodex proxy -> xai/grok-4.6 OAuth Responses. Run a long tool-using thread until an upstream 502 / connection-reset, then continue the same thread.

Equivalent replay body the proxy currently forwards after an interrupted tool turn (redacted):

{
  "model": "grok-4.6",
  "stream": true,
  "input": [
    { "type": "message", "role": "user", "content": [{ "type": "input_text", "text": "continue the task" }] },
    { "type": "function_call", "call_id": "call_interrupted", "name": "exec_command", "arguments": "{}" },
    { "type": "message", "role": "developer", "content": [{ "type": "input_text", "text": "[hook] LSP diagnostics: none" }] },
    { "type": "function_call_output", "call_id": "call_other", "output": "ok" }
  ]
}

Actual response or error

HTTP 502 / connection-reset on the first interrupted tool turn (often ~15s).
Later turns in the same thread then 400 or fail closed because the replayed input still contains
a function_call with no matching function_call_output, or a hook-injected developer message
between the call and its result.

Upstream documentation

Suggested mapping or implementation notes

Seed requiresAdjacentResponsesToolResults: true on the xai registry entry. Keep statelessResponses unset. In src/adapters/openai-responses/passthrough.ts, run the existing repairOrphanedInputItems(..., synthesizeMissingCallOutputs) for non-forward adjacency providers, not only for statelessResponses. Cursor is a separate protobuf adapter and should stay out of this change.

Additional context and attachments

Related: #2199, #4726, #1292, #2156, #3384, #186, #4191, #4808, #3506. Cursor 429 / empty-spin is a different adapter; not requested here. The 15s connection-reset cluster is an upstream cut-off and should stay a separate issue.

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

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 reportstoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions