Repository navigation
Conversation
Symptoms: Responses WebSocket connections can close immediately after a successful 101 handshake, causing clients to report "Connection reset without closing handshake" and repeatedly reconnect. Different worker initialization orders can produce alternating successful and failed connections. Cause: CCH and Next.js can both handle the same WebSocket upgrade event. After CCH completes the handshake, the Next.js listener can close the accepted socket. Solution: Route /v1/responses upgrades exclusively to CCH before invoking ordinary upgrade listeners. Preserve listener dispatch for other paths, the current worker's private HTTP loopback target, payload limits, and cleanup behavior.
Symptoms: Responses WebSocket continuations can fail with previous_response_not_found while the client connection remains open. Remote Compaction v2 can also be recorded as successful after a structured WebSocket error. Cause: Logical session affinity does not guarantee reuse of the same physical upstream WebSocket. Connection, routing, or credential changes can lose connection-local history while requests still carry previous_response_id. Structured WebSocket failures can also bypass final error accounting. Solution: Bind protected continuations to the live upstream connection, provider, endpoint, generation, and complete request-header fingerprint. Recheck access and limits, preserve prior storage semantics, and reject invalid bindings with a canonical 400 error requiring full-context replay. Prevent alternate routing, retries, HTTP fallback, hedging, Replay, and fake streaming for protected continuations. Confirm terminal delivery before closing, discard queued continuations, and preserve attempt-local error accounting with idempotent resource cleanup.
Extend the trusted WebSocket outcome used by connection-local continuation protection to genuine upstream terminal errors. Record raw compaction failures and release attempt resources without successful completion side effects. Preserve continuation-specific 400/409 handling, ordinary HTTP passthrough, and normal response diagnostics.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
72500b1 to
a9adfb3
Compare
Summary
Fix incorrect success accounting for Responses WebSocket requests that fail during raw compaction. Upstream
errorandresponse.failedevents were being recorded as successful HTTP 200 requests because the raw compaction path skipped response-body diagnostics. This is the final PR in a three-part series fixing Responses WebSocket reliability.Problem
Genuine upstream WebSocket
errorandresponse.failedevents can be recorded as successful HTTP 200 requests during raw Responses compaction because that path skips general response-body diagnostics. This means circuit breakers, rate limiters, and usage accounting see a "success" when the upstream actually failed, hiding provider health problems.Related Issues:
Solution
Extend the continuation-protection trusted outcome (introduced by #1517) with the first genuine upstream terminal error's code and status. The raw compaction finalization path now:
Preserved behaviors: continuation-specific 400/409 handling, ordinary opaque HTTP responses, normal response diagnostics, and adapter-generated disconnect handling. The streamed HTTP response may already be 200; this change corrects only the final failure accounting.
Changes
Core Changes
src/app/v1/_lib/proxy/forwarder.ts(+229/-66) - Major rework: bind continuation requests to their upstream connection, prevent retry/migration for protected continuations, record terminal errors from upstream WebSocket eventssrc/app/v1/_lib/proxy/response-handler.ts(+49/-17) - Endpoint-level circuit isolation; respect trusted outcome for failure accounting in raw compaction finalizationsrc/app/v1/_lib/responses-ws/upstream-adapter.ts(+160/-33) - Track terminal error state from upstreamerror/response.failedevents; expose outcome through trusted headerssrc/app/v1/_lib/responses-ws/continuation.ts(new, +84) - Connection-local continuation binding: WeakMap-based session binding with provider, endpoint, generation, and header fingerprint verificationsrc/app/v1/_lib/proxy/error-handler.ts(+54) - Durable persistence of failure outcomes during raw compactionsrc/app/v1/_lib/proxy/errors.ts(+12) - NewResponsesWsContinuationErrorfor structuredprevious_response_not_founderrorssrc/app/v1/_lib/proxy/provider-selector.ts(+52/-9) - Session affinity awareness;selectProviderByTypefor continuation binding verificationInfrastructure Changes
server.js(+131/-37) - Exclusive/v1/responsesupgrade handler installed before Next.js listeners; private HTTP bridge isolation; clean shutdown with close-handshake supportsrc/app/v1/_lib/responses-ws/internal-secret.ts(+6) - Internal trust header for connection-local WebSocket requestsSupporting Changes
src/app/v1/_lib/proxy/fake-streaming/proxy-integration.ts(+4) - Guard against fake streaming for WS continuationssrc/app/v1/_lib/proxy/replay/replay-identity.ts(+3) - Guard against Replay for WS continuationssrc/app/v1/_lib/proxy/stream-finalization.ts(+3) - One-time resource release guardsrc/lib/utils/error-messages.ts(+1) - New error keyRESPONSES_WS_PREVIOUS_RESPONSE_NOT_FOUNDmessages/{en,ja,ru,zh-CN,zh-TW}/errors.json- i18n for the new error message (5 languages)Series and Review
Part 3 of 3; depends on #1517, which depends on #1516. Keep this PR as a draft until its predecessors are merged and this branch is synchronized.
All three PRs target
dev, so this branch initially includes all three commits. Merge order: #1516, #1517, then this PR.Incremental review: a9adfb36.
Testing
Automated Tests (16 test files, 161+ new assertions)
New test files:
responses-ws/__tests__/continuation.test.ts- Continuation binding creation, verification, and rejectionresponses-ws/__tests__/upstream-adapter.test.ts- Terminal error tracking from upstream events, outcome state propagationserver-ws-upgrade-routing.test.ts- Exclusive upgrade handler routing, listener ordering, delegation of non-WS pathsserver-ws-continuation.test.ts- End-to-end connection-local continuation with binding validationfake-streaming-ws-continuation.test.ts- Fake streaming guard for WS continuationsresponse-handler-endpoint-circuit-isolation.test.ts- Circuit breaker isolation respects failure outcomesExtended test files:
error-handler-durable-persistence.test.ts- Raw compaction failure accounting with one-time resource releaseproxy-forwarder-raw-passthrough-regression.test.ts- Retry prevention for bound continuationsproxy-forwarder-retry-limit.test.ts- Retry limit interaction with continuation bindingprovider-selector-affinity-ignore-session.test.ts- Session affinity for continuation-bound requestsresponse-handler-stream-terminal.test.ts- Terminal error propagation through stream finalizationreplay-identity.test.ts- Replay guard for WS continuationsserver-ws-close-handshake.test.ts- Graceful close handshake during shutdownChecklist
Description enhanced by Claude AI
Publication validation
Production build, lint, lint auto-fix, typecheck, and full Vitest passed on the cumulative series tip in an isolated checkout (9,548 tests passed, 13 skipped). Every rewritten commit retains identical code, tests, translations, and configuration.