Skip to content

test(executor): fix two websocket test-server races behind CI flakes (MASTRA-771) - #6

Merged
ericlitman merged 1 commit into
mainfrom
claude/MASTRA-771-websocket-flake
Oct 3, 2026
Merged

ericlitman merged 1 commit into
mainfrom
claude/MASTRA-771-websocket-flake

Conversation

@ericlitman

@ericlitman ericlitman commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Linear: MASTRA-771

Fixes the websocket test flakes that failed about every other go job in this repo's CI. Both are races in the test servers, not in production code. The fix is test-only: 3 files, +32/−29.

Root causes, each reproduced

1. Keepalive tests (Test{Codex,XAI}Websockets_KeepalivePingDuringUpload_*, 5 tests)

  • The fake upstream replied and closed as soon as the pong arrived, before the client's held payload write had run.
  • The client's readUpstreamLoop (codex_websockets_session.go:665-688) saw the close and called invalidateUpstreamConn (:774), so the released write failed with "use of closed network connection".
  • In the WithSession variants, the retry path called the test hook a second time, which panics with "close of closed channel".
  • Fix: the server waits until its reader has received the request before replying.

2. Retry bind test (TestWebsocketRetryBindFailureClearsActiveSessionState)

  • The server numbered connections after Upgrade. If connection 1's handler was descheduled after the handshake, the next dial took number 2 and was closed at once.
  • Fix: count before Upgrade. The 101 response is sent only after the count, so numbers follow dial order.

Evidence

Reproducer: GOMAXPROCS=1 stress, -p 32, with 16 CPU burners.

Tests Before After
Keepalive pair 153 failures in 20,000 runs (0.77%) 0 in 60,000
Retry bind 52 failures in 6,000 (0.87%) 0 in 18,000
  • Forced ordering (a sleep injected at the race point) failed every run before the fix and none after.
  • -race at GOMAXPROCS=2 found no data race, before or after. So the shared hook variable is not the cause.
  • Package under load: 10 runs, 0 failures. go test ./...: 100 packages ok. gofmt and go vet are clean.

Upstream

git diff v8.0.13 main -- internal/runtime/executor/ is empty, so both races exist unchanged in router-for-me/CLIProxyAPI v8.0.13. This commit applies there as-is. Upstream's CI only runs go build, so it never sees them.

Not changed (observation)

TestXAIWebsocketsExecuteStreamSendsResponseCreateWithPreviousResponseID leaves a stream goroutine holding a global session lock, so it hangs at -count>=2. CI uses -count=1 and is unaffected.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith with what you need. Autofix is disabled.

…(MASTRA-771)

Root cause 1, keepalive tests (Codex and xAI KeepalivePingDuringUpload
WithSession, Sessionless and NonstreamSessionless): the fake upstream
wrote response.completed and closed the connection as soon as the pong
arrived, before the client's held payload write ran. The client read
loop (readUpstreamLoop) then saw the close and invalidated the
connection, so the write failed with "use of closed network
connection". The server now replies only after it has read the request.
In the WithSession variants the same race would send the retry back
through the hook and panic on a second close(inWriteHook).

Root cause 2, TestWebsocketRetryBindFailureClearsActiveSessionState:
the server numbered connections after Upgrade, so a handler descheduled
after the handshake could take a later number. The third connection
then got number 2, was closed at once, and the second request failed
with close 1006. The counter now runs before Upgrade, in dial order.

Evidence (GOMAXPROCS=1, stress -p 32, 16 CPU burners):
- keepalive Sessionless tests: 153/20000 failures before, all the CI
  signature; 0/60000 after.
- retry bind test: 52/6000 before (1006 or reset on the second
  request); 0/18000 after.
- Deterministic: a 100 ms sleep after the pong wait fails 25/25 runs
  before and 0/25 after. A 200 ms sleep after the first Upgrade fails
  15/15 before and 0/15 after.

Production code is unchanged. internal/runtime/executor is identical to
upstream v8.0.13.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Oct 3, 2026 •

Copy link
Copy Markdown
MASTRA-771 Fix the flaky upstream websocket tests in mobilyze-llc/CLIProxyAPI CI

Context

mobilyze-llc/CLIProxyAPI CI runs go test ./...; upstream's CI only runs go build. The websocket tests in internal/runtime/executor fail intermittently in our CI with "use of closed network connection" or similar, and pass on rerun. Eric decided to fix them (2026-10-03).

Failures so far, each passing on rerun:

Facts read from source:

  • The tests swap a package-level hook, testWebsocketWritePayloadHook, which production code reads unsynchronised at codex_websockets_session.go:178.
  • Twelve tests set it: 8 Codex, 4 xAI.
  • Parts of the package use t.Parallel().
  • The tests depend on 2 s timing between client writes and server pings.

Local isolated runs (50×) and three loaded package runs did not reproduce it. The root cause is not yet established.

Scope

  1. Reproduce: find load or flags that reproduce the failure locally or in CI (for example -race, -cpu, GOMAXPROCS, -count, running alongside the rest of ./...).
  2. Establish the root cause with evidence: a race report, logs, or a deterministic reproducer.
  3. Fix it in the tests, or in the code if a real product race exists, with the smallest change. Do not add retries, longer sleeps or skips.
  4. If the fix is a genuine upstream defect, offer it upstream as a PR.

Acceptance criteria

  • The root cause is stated with the evidence that proves it.
  • A reproducer fails before the fix and passes after (for example N repeated runs under the reproducing load).
  • 10 consecutive green go jobs in CI on the fix branch, or an equivalent repeated-run proof.
  • No test is skipped or loosened, and no retry or sleep is added to pass.

Source refs

  • Parent: MASTRA-763. Related: MASTRA-765 (the flake record and its correction are in comments), MASTRA-768.

Review in Linear

@ericlitman
ericlitman merged commit 3931a30 into main Oct 3, 2026
3 checks passed
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