Skip to content

fix(stream): retry pre-output TLS close_notify EOF and abort thinking-only streams on transient drops - #210

Open
chris7iu wants to merge 1 commit into
raine:mainfrom
chris7iu:fix/stream-blip-retry-and-thinking-abort
Open

chris7iu wants to merge 1 commit into
raine:mainfrom
chris7iu:fix/stream-blip-retry-and-thinking-abort

Conversation

@chris7iu

Copy link
Copy Markdown
Contributor

Summary

  • Pre-output Codex TLS close_notify / unexpected-eof retry: Classifies rustls's statusless peer closed connection without sending TLS close_notify (#unexpected-eof) transport failure as retryable in providers::codex::events::retryable_message so claude-code-proxy automatically retries pre-semantic WebSocket/HTTP drops within send_with_retry (while keeping *_policy / *_policy_violation event errors Permanent 400).
  • Mid-thinking downstream chunked-body abort across all streaming providers (codex, grok, opencode, cursor): When an upstream stream fails mid-turn with a retryable transport or transient (5xx/529) error after emitting thinking deltas (HTTP 200 headers already flushed) before any visible text, tool_use, or server_tool_use content block has started, aborts the downstream HTTP chunked body with Err(std::io::Error) instead of synthesizing event: error + clean EOF.
    • Matching native Anthropic/Claude Code behavior: Claude Code's withRetry treats a synthetic SSE event: error + clean 0\r\n\r\n EOF as a terminal APIError and stops the turn, whereas an aborted chunked stream surfaces as UND_ERR_SOCKET (APIConnectionError) and triggers seamless turn-level retry (maxRetries = 10, exponential backoff from 500ms), discarding any uncommitted thinking blocks on the discarded attempt.
    • Once any visible output (text, tool_use, server_tool_use) has started—or when the upstream failure is non-retryable (4xx, policy violation, invalid prompt, auth/quota)—continues emitting event: error + clean EOF to avoid duplicating side effects or retrying deterministic rejections.

Test Plan

  • cargo fmt --all -- --check
  • cargo clippy --all-targets -- -D warnings
  • cargo test --all --locked
    • Added unit and end-to-end HTTP chunked-wire tests covering:
      • rustls close_notify / unexpected-eof retry classification and cyber_policy_violation permanent classification (providers::codex::client::tests)
      • Codex WebSocket/SSE mid-thinking transient drop -> aborted HTTP chunked body vs. mid-text transient drop -> event: error + clean EOF vs. mid-thinking permanent cyber_policy_violation -> event: error + clean EOF (providers::codex::tests)
      • Grok mid-thinking stream read error -> aborted HTTP chunked body vs. mid-text stream read error -> event: error (providers::grok::tests)
      • OpenCode (messages, chat, responses) and Cursor visible-output tracking and mid-thinking chunked-body abort

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant