Skip to content

xai: grok-4.6 websocket 1006 kills session drain #48690

Closed

Description

@sankalpmukim

Summary

xAI Grok 4.6 Responses WebSocket drops mid-turn with close code 1006. OpenCode classifies this as provider.transport, SessionRunner.drain fails, and the TUI surfaces Error: WebSocket closed with code 1006 instead of retrying or falling back to HTTP.

Environment

  • opencode version: 2.0.2
  • OS: Darwin 24.6.0 (darwin arm64)
  • Terminal: TERM_PROGRAM=ghostty, TERM=xterm-256color, COLORTERM=truecolor
  • Shell: /opt/homebrew/bin/fish
  • Install/channel: latest
  • Active plugins: @mohak34/opencode-notifier@latest (global); local TUI plugin herdr-tui-session.js (unrelated)

Reproduction

Intermittent. Observed three times in one evening on long xai/grok-4.6 sessions with many tool rounds.

  1. Use model xai/grok-4.6 (variant medium) in the TUI against the shared background service.
  2. Run a long agent turn with many tool calls (local shell / MCP). Context grows; xAI Responses WebSocket stays open for the session.
  3. Mid-step, the provider socket closes abnormally.
  4. TUI shows Error: WebSocket closed with code 1006. The turn ends. Sending another prompt / continue can resume later, but the in-flight step is lost.

Expected Behavior

A transient WebSocket 1006 during an in-flight xAI Responses stream should be treated as retryable transport (AI.Error.Transport, operation: "read"):

  • If no assistant output has been committed, retry or fall back to HTTP (same idea as the SSE workaround / OpenAI WS retry path).
  • If partial output already streamed, do not replay; surface a continue-after-partial-output path instead of killing drain.
  • Docs currently say a closed socket reconnects on the next step and a failed open falls back to HTTP. Mid-stream close should not hard-fail SessionRunner.drain.

Actual Behavior

Server log (role=server):

level=ERROR message="Failed to drain Session"
cause="AI.Error: WebSocket closed with code 1006
  [cause]: AI.Error.Transport: WebSocket closed with code 1006
  at SessionStep.attempt
  at SessionRunner.runStep
  at SessionRunner.drain"

Persisted assistant message:

{
  "finish": "error",
  "error": {
    "type": "provider.transport",
    "message": "WebSocket closed with code 1006"
  },
  "model": { "id": "grok-4.6", "providerID": "xai", "variant": "medium" }
}

A sibling failure on the same session was stored as "message": "Session WebSocket closed" (provider.transport).

TUI displays the transport error verbatim. This is not the TUI losing its local OpenCode service WebSocket.

Observed in session_v2 (local DB, redacted paths):

session idle_outcome error time (UTC) message
ses_f692903d5ffe… interrupted 2026-09-12 18:22:14 WebSocket closed with code 1006
ses_f6922555dffe… failed 2026-09-12 18:26:14 Session WebSocket closed
ses_f6922555dffe… failed 2026-09-12 18:31:20 WebSocket closed with code 1006

Drops happened ~5–6 minutes into the session, so not xAI’s documented 25-minute socket cap. Same machine previously recorded xAI ECONNRESET / ENOTFOUND as provider.transport on grok-4.5.

Additional Context

Suggested fix: classify mid-stream xAI/OpenAI WS close 1006 as retryable Transport before drain failure; HTTP-fallback when no output committed; reconnect-or-continue when output already streamed.

Activity

  1. sankalpmukim commented on Sep 12, 2026

    @sankalpmukim
    Author

    Follow-up: not @ai-sdk/xai; provider websocket: false is ignored for Grok

    Reproduced on 2.0.2 after setting providers.xai.websocket: false. Live GET /api/provider/xai returned "websocket": false, then a new session still died with the same error.

    Babysit session ses_f6906ee09ffeEfVdKX2PPKsh4o (xai/grok-4.6#medium, prompt get to work): start 2026-09-12T18:55:00Z → provider.transport WebSocket closed with code 1006 at 18:58:30Z (~3.5 min, many tool/image rounds). idle_outcome=failed.

    Where the error is actually thrown

    Exact message is OpenCode’s own transport, v2.0.2 packages/ai/src/route/transport/websocket.ts:

    transportError(`WebSocket closed with code ${event.code}`, {
      operation: "read",
      code: String(event.code),
      phase: "close",
    })

    That is @opencode/ai WebSocketExecutor on the Responses WebSocket (wss://…/v1/responses), not a TUI↔server socket.

    @ai-sdk/xai is not the chat WebSocket

    Checked vercel/ai packages/xai (published @ai-sdk/[email protected]):

    • Chat / Responses use HTTP fetch only (OpenCode’s provider-xai-responses.test.ts mocks fetch on .responses()).
    • SDK WebSockets are for streaming STT / realtime, not Grok coding turns.

    Why provider-level websocket: false does nothing on xAI

    XAIPlugin stamps every xAI model (packages/core/src/plugin/provider/xai.ts):

    draft.capabilities.responsesWebsockets = true
    draft.websocket = true

    Catalog merge is model.websocket ?? provider.websocket (packages/core/src/catalog.ts). Model true wins, so provider false is ignored. ModelResolver uses selected.websocket. Test enables xAI Responses WebSockets expects grok-4.6.websocket === true.

    ConfigProviderPlugin runs later (post) but only writes model.websocket when set on the model. Same stamp exists on OpenAI models.

    Expected working config:

    {
      "providers": {
        "xai": {
          "websocket": false,
          "models": {
            "grok-4.6": { "websocket": false }
          }
        }
      }
    }

    Provider-only false is a documented no-op for Grok until the plugin stops forcing model-level true, or merge treats provider false as an override.

    Still needed: treat mid-stream close 1006 as retryable Transport (operation: "read") and HTTP-fallback when no output is committed.

  2. rekram1-node commented on Sep 22, 2026

    @rekram1-node
    Collaborator

    Fixed by #49183 and released in v2.0.5: acknowledged WebSocket read failures such as close code 1006 now retry before durable output and continue from durable history after partial output. #49350 (v2.0.6) also made provider-level HTTP transport overrides work, and #50031 (v2.0.11) pins a session to HTTP after repeated WebSocket stream losses. Closing as fixed.

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions