Skip to content

Codex executor returns retryable 408 when stream closes before response.completed #3055

Closed

Description

@zzl360

Summary

When using the Claude-compatible /v1/messages route with a Codex-backed model alias, some large requests fail because the upstream stream closes before a final response.completed event is received.

CLIProxyAPI returns HTTP 408 for this case:

stream error: stream disconnected before completion: stream closed before response.completed

Because 408 is treated as retryable, the same request can be retried across credentials. This may cause multiple auth entries to become unavailable even though the failure appears to be request-specific.

Environment

  • CLIProxyAPI version: v6.9.37
  • Route: /v1/messages
  • Client: Claude Code
  • Backend/executor: Codex executor
  • Model alias example: gpt-5.5
  • Streaming enabled
  • Request size observed: around 1.1MB - 1.3MB

Relevant config:

request-retry: 1
max-retry-credentials: 1
max-retry-interval: 30

streaming:
  keepalive-seconds: 15
  bootstrap-retries: 1

routing:
  strategy: "round-robin"
  session-affinity: true
  session-affinity-ttl: "1h"

Actual behavior

Large Claude Code requests routed through the Codex executor repeatedly fail with:

status=408
error=stream error: stream disconnected before completion: stream closed before response.completed

Observed log pattern:

status=408
content_length≈1155106-1321124
session=<same-session-id>
error=stream error: stream disconnected before completion: stream closed before response.completed

After repeated attempts, multiple auth entries can become unavailable:

auth_unavailable=4/6
auth_unavailable=5/6

Expected behavior

A request-specific stream completion failure should not cause broad credential rotation or make multiple auth entries unavailable.

Additional notes

Enabling streaming keepalive did not resolve the issue:

streaming:
  keepalive-seconds: 15
  bootstrap-retries: 1

Limiting credential retries reduced the blast radius but did not eliminate the problem:

request-retry: 1
max-retry-credentials: 1

Related PRs checked:

Activity

  1. zzl360 commented on Apr 26, 2026

    @zzl360
    Author
  2. zrz-orz commented on Apr 27, 2026

    @zrz-orz

    请问这个问题解决了吗?

  3. zzl360 commented on Apr 27, 2026

    @zzl360
    Author

    请问这个问题解决了吗?

    我是让大模型帮我改了下,遇到这个问题的时候,返回了400或者其他的错误值,提示需要压缩。算是缓解了这个问题,但最好还是官网在上游解决这个问题

  4. RYQ-22 commented on May 6, 2026

    @RYQ-22

    same here, especially with the gpt-5.5 model. I didn't run into this issue with gpt-5.4, but the performance of gpt-5.5 is much better. Now I have to start a new session whenever the context is long enough to trigger this issue.

  5. WoChen5770 commented on Jun 17, 2026

    @WoChen5770

    上游是newapi的现在基本上都会报这个错误了

  6. 265866 commented on Jul 1, 2026

    @265866

    Hi, also getting this error w/ gpt5.5 on longer context sessions

  7. added a commit that references this issue on Jul 15, 2026
    09da52a
  8. added a commit that references this issue on Jul 21, 2026
    dd9aa4b
  9. fashen97 commented on Jul 23, 2026

    @fashen97

    New reproduction on newer versions

    I can reproduce the same client-visible failure on a much newer setup:

    • CLIProxyAPI: v7.2.95
    • OpenCode: v1.18.4
    • Model: gpt-5.6-terra
    • Platform: macOS arm64
    • request-retry: 3
    • max-retry-credentials: 5
    • Session affinity enabled
    • streaming.keepalive-seconds: 15
    • streaming.bootstrap-retries: 3

    The client receives HTTP 408 before any usable Responses event:

    {"type":"error","sequence_number":0,"code":"request_timeout","message":"stream error: stream disconnected before completion: stream closed before response.completed"}
    HTTP 408
    stream error: stream disconnected before completion: stream closed before response.completed
    

    The sequence_number is 0; OpenCode has not received a usable response event and terminates the turn, so the user must manually resubmit the task.

    One affected upstream request returned these correlation headers:

    Date: Thu, 23 Jul 2026 07:03:17 GMT
    X-OAI-Request-ID: 5a9e96f3-d136-4e1b-8bbe-def413f5b48e
    CF-Ray: a1f8d3048986acf2-NRT
    X-Codex-Plan-Type: free
    X-Codex-Primary-Used-Percent: 79
    

    This was not a normal HTTP 429 rate-limit response.

    Additional network observation: CPA traffic to chatgpt.com is transparently routed through a local TUN/Fake-IP proxy path. In an 8-request HTTPS probe, the first TLS handshake took about 3.9 seconds and the following requests took about 0.13-0.19 seconds. An explicit local HTTP proxy path was consistently about 0.11-0.17 seconds for TLS. I do not claim this proves a network-only cause, but it may matter when CPA opens fresh upstream connections.

    Could a maintainer please help determine whether CPA is seeing an upstream clean EOF, an underlying transport/read error, or losing the terminal event while parsing or forwarding the stream? The client-visible 408 does not distinguish these cases.

    Could this issue be reopened, or linked to the relevant fix if one already exists? I can provide redacted debug logs or raw final SSE frames if you can specify the useful fields.

  10. added a commit that references this issue on Jul 24, 2026
    68ad96f
  11. added a commit that references this issue on Jul 24, 2026
    f9e9366
  12. RoySalisbury commented on Sep 15, 2026

    @RoySalisbury

    Additional reproduction on CLIProxyAPI v7.3.3 (7bbfeaf) with OpenCode 1.18.29, with a specific downstream error-classification observation.

    Route: OpenCode → /v1/chat/completions → CLIProxyAPI → Codex upstream.

    The proxy-side request/response log shows:

    • Upstream HTTP 200, followed by response.created, response.in_progress, and many response.output_text.delta events.
    • Substantial partial text was already forwarded downstream.
    • At approximately 25.3 seconds total request duration, the upstream stream ended without response.completed.
    • CLIProxyAPI logged stream error: stream disconnected before completion: stream closed before response.completed.
    • Downstream remained HTTP 200 and ended with:
      data: {"error":{"message":"stream error: stream disconnected before completion: stream closed before response.completed","type":"invalid_request_error"}}
      

    Configured protections:

    request-retry: 3
    max-retry-interval: 20
    streaming:
      keepalive-seconds: 15
    codex:
      stream-bootstrap-buffering: true

    The user observed OpenCode terminating the turn without automatic recovery. Client-side normalized error and retry traces have not yet been captured; we are not claiming a proven internal client classification path or an established root cause for the upstream disconnect.

    Question: should a premature upstream stream disconnect be emitted with a stable transient stream/transport error type and code, rather than invalid_request_error with no code? That label describes this transport/completion failure as a bad request and may prevent downstream clients from recognizing it as recoverable.

    This example is after generated output, so we are not asking the proxy to transparently replay the generation or treating bootstrap retries as a fix. Downstream recovery needs to preserve partial progress and avoid replaying completed tool actions.

    Corresponding OpenCode report with the sanitized sequence:
    anomalyco/opencode#30611 (comment)

    Full request logs are intentionally omitted because they contain conversation content and headers.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions