Skip to content

[Bug]: ChatGPT upstream WS closes 1008 after 1,589 output events on 2.84.0 #7073

Open

Description

@tone-leh

Client or integration

Codex App

Area

Streaming

Summary

On OpenCodex 2.84.0, a canonical ChatGPT Responses WebSocket received 1,589 relayed events, then closed with code 1008 before any terminal Responses event. The logical request ended as HTTP 502 / synthetic mid-stream failure. This is the after-output case explicitly left open for a separate report in #4191 (#4191 (comment)), not the pre-output fallback fixed by #5675.

The immediate host mitigation is the native providers.openai.upstreamWebsocket=false option from #5704 / #5738. I am reporting the observed WS failure and diagnostic limits; I cannot yet attribute the upstream policy decision or claim a deterministic OCX defect. Please investigate whether a WS protocol/compatibility issue explains this close. Automatically replaying an already-producing request would be unsafe and is not the requested fix.

Reproduction

Intermittent production observation, not a deterministic minimal reproduction:

  1. Codex App continued a long tool-using conversation through the canonical openai provider in pool mode; upstreamWebsocket was omitted (native WS default).
  2. The submitted create frame was 2,901,178 bytes. First frame arrived at 1,597 ms; first Responses event at 2,628 ms.
  3. After 60,277 ms and substantial output, the upstream socket emitted close 1008 without a completed/failed/incomplete terminal event.
  4. The client displayed stream-disconnected/reconnection UI.

No live replay or fault injection was attempted. An offline fake-socket harness confirms the official exchange surfaces close1008 after output without issuing an HTTP replacement. Other natural WS requests on this instance completed at >60 s (including ~180 s), so this is not evidence of a universal 60-second timeout.

Version

@bitkyc08/opencodex 2.84.0; bundled Bun 1.4.2 (744846f84).

Operating system

macOS 27.0.1, arm64.

Provider and model

Canonical ChatGPT openai (subscription account pool) / gpt-6-astra.

Logs or error output

Content-free stage record from one attempt (2026-10-13 16:20:41.777 UTC request start):

{
  "status": 502,
  "durationMs": 61782,
  "transportPhase": "mid_stream",
  "terminalSource": "synthetic",
  "sendCount": 1,
  "recoveryKinds": [],
  "errorCode": "upstream_server_error",
  "failureStage": "semantic-output",
  "failureCause": "transport-ambiguous",
  "codexWsStage": {
    "requestBytes": 2901178,
    "sent": true,
    "upstreamFrames": 1591,
    "controlFrames": 2,
    "relayedEvents": 1589,
    "firstFrameMs": 1597,
    "firstResponseMs": 2628,
    "elapsedMs": 60277,
    "pings": 0,
    "pongs": 0,
    "closeCode": 1008,
    "reused": false,
    "ocxVersion": "2.84.0",
    "bunVersion": "1.4.2",
    "terminalEvent": null,
    "terminalErrorStatus": null
  }
}

Screenshots and supporting files

Static investigation:

  • ws-upstream.ts, codex-ws-exchange.ts, codex-ws-session.ts, fetch-helpers.ts, and management/provider-routes.ts match the pristine 2.84.0 npm package byte-for-byte. This host does have local overlays in other routing/maintenance areas; it is not an entirely unmodified installation.
  • The proxy PID was unchanged across the failing request; no restart occurred during this request.
  • The create frame is below the native 16 MiB send bound; the socket was not reused.
  • This is not a first-event timeout: response committed at ~2.6 s. Ping/silence timers stop on response commit in the shipped implementation; pings=0 alone is not evidence of a missing-heartbeat cause.
  • Durable stage telemetry intentionally omits raw close reason. No reason is available in this captured error. We do not infer quota exhaustion from 1008.
  • Native single-field management PATCH selected HTTP/SSE without restarting the process. The only persistent config difference was that field. Two subsequent naturally occurring requests completed 200 / upstream terminal with no codexWsStage. This is limited mitigation evidence, not a long-duration stability claim.
  • Six isolated checks pass: default vs opt-out selection, single HTTP dispatch, in-flight WS continuity when replacing the provider row, no unsafe post-output replay, rollback on save failure, and WS-only control eligibility.

Redacted configuration

Before:

{
  "websockets": false,
  "providers": {
    "openai": {
      "adapter": "openai-responses",
      "authMode": "forward",
      "baseUrl": "https://chatgpt.com/backend-api/codex",
      "codexAccountMode": "pool"
    }
  }
}

After mitigation: add only providers.openai.upstreamWebsocket: false. Models, credentials and account-pool policy are unchanged. Client-facing websockets is a separate switch.

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

  1. added
    bugSomething isn't working
    streamingSSE, WebSocket, terminal stream frames
    on Oct 13, 2026
  2. tone-leh commented on Oct 14, 2026

    @tone-leh
    ContributorAuthor

    Additional passive evidence after setting the native providers.openai.upstreamWebsocket=false on the same 2.84.0 instance. This bypasses the observed upstream WS 1008 path; it does not establish that all upstream failures are fixed.

    No new model probes or reproduction tests were run. The proxy process remained the same throughout the observation window. In the existing completed usage ledger after the change: 434 native openai records, comprising 427 status-200 records, two 429s, four 502s, and one 507. Status-200 counts are ledger observations, not a claim of full client/Goal acceptance.

    Content-free details of the seven failed records:

    Count HTTP / code Recorded evidence
    2 429 / upstream_reset_replay_refused failureStage=headers-only, sendCount=1; request may already have been processed, automatic replay refused. The legacy failureCause=rate-limit is not proof of quota exhaustion.
    2 502 / upstream_server_error Provider unreachable: unknown certificate verification error; around 5.03 seconds, sendCount=1, headers-only.
    2 502 / upstream_server_error terminalSource=synthetic, terminalStatus=failed, transportPhase=mid_stream, failureStage=protocol-prelude, failureCause=transport-ambiguous; attempt streamAborted=true, sendCount=1; 55.100 and 82.631 seconds. No underlying error string was retained in those ledger rows.
    1 507 / upstream_server_error exceeded request buffer limit while retrying upstream, headers-only, sendCount=1, 1.870 seconds. No request body captured.

    The first six were already present in the earlier review; the 507 is a later natural request. There was no proxy reload/restart or account/model switch in this follow-up. Bundled Bun is 1.4.2 on macOS arm64. The inspected fetch/TLS modules match the official 2.84.0 package. Other local patches exist, so this is not presented as a pristine end-to-end reproduction.

    I read #6740, #6658 and #1419. In particular #1419 documents that abruptly reset TLS handshakes can surface as UNKNOWN_CERTIFICATE_VERIFICATION_ERROR in Bun. Therefore the two TLS messages alone do not prove an invalid trust store, and no TLS verification was disabled. The synthetic protocol-prelude failures also do not establish an upstream semantic terminal or justify replay.

    Keeping #7073's root cause unresolved: the WS transport workaround only avoids that route. These HTTP/SSE categories may have separate causes. Is there an existing upstream issue or content-free diagnostic field that can distinguish handshake abort versus certificate-chain failure, and retain the terminal transport cause without replaying requests or resetting session/account affinity?

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

    bugSomething isn't workingstreamingSSE, WebSocket, terminal stream frames

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions