Summary
When a provider upstream is not one of the uTLS-fingerprinted hosts (api.anthropic.com, chatgpt.com) — e.g. a reseller/aggregator gateway, a self-hosted relay, or any OpenAI/Anthropic-compatible endpoint — requests intermittently fail mid-stream and the client receives a raw Go error:
API Error: stream error: stream ID 1; INTERNAL_ERROR; received from peer
This happens occasionally (only when the upstream resets a stream mid-response), so it presents as flaky "stream cut off" behavior.
Root cause
That string is the golang.org/x/net/http2 StreamError with errFromPeer ("received from peer") — i.e. a Go HTTP/2 client received a RST_STREAM(INTERNAL_ERROR) frame from its server. The only Go HTTP/2 client in the request path is CLIProxyAPI's own connection to the upstream.
The uTLS Chrome-fingerprint HTTP/2 round-tripper is gated to a hardcoded allow-list (internal/runtime/executor/helps/utls_client.go):
var utlsProtectedHosts = map[string]struct{}{
"api.anthropic.com": {},
"chatgpt.com": {},
}
Every other host falls through to fallback = http.DefaultTransport, which has ForceAttemptHTTP2 = true and negotiates HTTP/2 whenever the upstream's ALPN offers h2 (most gateways behind nginx/Cloudflare do).
When such an upstream resets a stream mid-response:
- The conductor only retries resets that occur before the first byte (
empty_stream … Retryable: true). A mid-stream reset cannot be retried (bytes already forwarded to the client).
- The abrupt
RST_STREAM is surfaced through the executor's scanner.Err() path and forwarded verbatim, so the client sees the raw stream error … INTERNAL_ERROR; received from peer.
Impact
Any deployment whose claude/codex/gemini/openai-compat upstream points at a non-anthropic/chatgpt HTTPS endpoint that negotiates HTTP/2 is exposed: a flaky upstream channel leaks raw http2 framing errors to end users instead of failing gracefully.
Proposed fix
Force HTTP/1.1 on the fallback transport (the non-fingerprinted path), mirroring the existing cloneTransportWithHTTP11 used by the antigravity executor. Over HTTP/1.1 the RST_STREAM error class cannot occur; a mid-response upstream drop becomes a plain connection close (clean EOF), which composes cleanly with terminal-marker synthesis (e.g. #3997) for a graceful message_stop/[DONE] instead of a hard error. The uTLS HTTP/2 path for Anthropic/ChatGPT is intentionally left untouched to preserve its Chrome fingerprint.
PR with tests: follows.
Environment
- CLIProxyAPI:
dev
- Upstream: third-party OpenAI/Anthropic-compatible aggregator (New API style) over HTTPS, ALPN
h2
- Downstream: nginx (TLS termination) → cliproxy over HTTP/1.1; client is Claude Code / OpenAI JS SDK
Summary
When a provider upstream is not one of the uTLS-fingerprinted hosts (
api.anthropic.com,chatgpt.com) — e.g. a reseller/aggregator gateway, a self-hosted relay, or any OpenAI/Anthropic-compatible endpoint — requests intermittently fail mid-stream and the client receives a raw Go error:This happens occasionally (only when the upstream resets a stream mid-response), so it presents as flaky "stream cut off" behavior.
Root cause
That string is the
golang.org/x/net/http2StreamErrorwitherrFromPeer("received from peer") — i.e. a Go HTTP/2 client received aRST_STREAM(INTERNAL_ERROR)frame from its server. The only Go HTTP/2 client in the request path is CLIProxyAPI's own connection to the upstream.The uTLS Chrome-fingerprint HTTP/2 round-tripper is gated to a hardcoded allow-list (
internal/runtime/executor/helps/utls_client.go):Every other host falls through to
fallback=http.DefaultTransport, which hasForceAttemptHTTP2 = trueand negotiates HTTP/2 whenever the upstream's ALPN offersh2(most gateways behind nginx/Cloudflare do).When such an upstream resets a stream mid-response:
empty_stream … Retryable: true). A mid-stream reset cannot be retried (bytes already forwarded to the client).RST_STREAMis surfaced through the executor'sscanner.Err()path and forwarded verbatim, so the client sees the rawstream error … INTERNAL_ERROR; received from peer.Impact
Any deployment whose claude/codex/gemini/openai-compat upstream points at a non-
anthropic/chatgptHTTPS endpoint that negotiates HTTP/2 is exposed: a flaky upstream channel leaks raw http2 framing errors to end users instead of failing gracefully.Proposed fix
Force HTTP/1.1 on the fallback transport (the non-fingerprinted path), mirroring the existing
cloneTransportWithHTTP11used by the antigravity executor. Over HTTP/1.1 theRST_STREAMerror class cannot occur; a mid-response upstream drop becomes a plain connection close (clean EOF), which composes cleanly with terminal-marker synthesis (e.g. #3997) for a gracefulmessage_stop/[DONE]instead of a hard error. The uTLS HTTP/2 path for Anthropic/ChatGPT is intentionally left untouched to preserve its Chrome fingerprint.PR with tests: follows.
Environment
devh2