Bind the vMCP test server's port directly - #6044
Merged
Merged
Conversation
The vmcp integration test helper picked a port by binding 127.0.0.1:0, reading the port back, and closing the listener — then handed that port to the vMCP server to bind later. Closing the probe listener releases the port, so between the close and the server's own net.Listen any of the ten t.Parallel() tests in this package (each also starting httptest backends) could be handed the same port by the OS. The result was an intermittent "bind: address already in use", which most recently failed CI on a PR whose diff was unrelated. Set ServerConfig.Port to 0 instead and read the bound address back from Address() after Ready(), which the helper already awaits. The server's listener is created and stored before the ready channel closes, so Address() reports the OS-assigned port to every caller. This removes the window rather than narrowing it: the port is never unbound between being chosen and being served. Closes #6034
JAORMX
requested review from
ChrisJBurns,
amirejaz,
jerm-dro,
jhrozek and
tgrunnagle
as code owners
July 27, 2026 18:12
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6044 +/- ##
==========================================
+ Coverage 72.16% 72.24% +0.07%
==========================================
Files 721 721
Lines 75069 75060 -9
==========================================
+ Hits 54174 54226 +52
+ Misses 17033 16958 -75
- Partials 3862 3876 +14 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
amirejaz
approved these changes
Jul 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
test/integration/vmcp/helpers.getFreePortpicked a port by binding127.0.0.1:0, reading the port back, and closing the listener — then handed that port to the vMCP server to bind later, inStart. Closing the probe listener releases the port, so between that close and the server's ownnet.Listenthe OS could hand the same port to any of the tent.Parallel()tests in this package (each of which also startshttptestbackends). The symptom was an intermittentbind: address already in usefailing CI on PRs with unrelated diffs — most recently Echo the request JSON-RPC id in session-not-found 404s #6031.ServerConfig.Portto0so the vMCP server's own listener picks the port and holds it, and read the bound address back fromvmcpServer.Address(), which the helper already waits for viaReady().getFreePortis deleted along with the now-unusednetimport. This closes the window rather than narrowing it: the port is never unbound between being chosen and being served.Closes #6034
Type of change
Test plan
task test— 3 pre-existing environmental failures, see below)task lint— 0 issues)Verified by code trace, not inferred from the doc comment:
Port: 0really does reach a port-0 bind.buildServeConfig(pkg/vmcp/server/serve.go:413) passescfg.Portstraight through;StartformatsaddrfromHost/Port(server.go:733) and callsnet.Listen("tcp", addr)(:745).Address()returns the OS-assigned port afterReady(). It returnss.listener.Addr().String()when the listener is non-nil (server.go:882-889), andStartstores the listener underlistenerMu(:750-752) beforereadyOnce.Do(close(s.ready))(:771). That ordering is what makes the fix correct — it is not incidental.Ready(). All 20 call sites intest/integration/vmcp/*_test.gobuild"http://" + vmcpServer.Address() + …afterNewVMCPServerreturns, and it only returns after<-vmcpServer.Ready().netbecomes unused and is removed;requireandtestingare still used elsewhere and stay.The original failure was reproduced before fixing it. On a clean tree with the old
getFreePortintact, amplifying the existing race (64 goroutines churning ephemeral-port allocation, plus a 3s delay widening the already-present window between the probeClose()and the server's bind) failed on the first run:A standalone harness modelling only the bind-close-rebind pattern under the same pressure gave 24 collisions / 31,773 rebind attempts (~0.08%), all
bind: address already in use.The fix removes it under the identical amplification that failed above: 3/3 runs green, 0 collisions.
Stress runs on the committed diff (no instrumentation):
go vet ./...task lint(golangci-lint +go vet ./...)0 issues.go test ./test/integration/vmcp/... -count=1 -raceok95.6sgo test ./test/integration/vmcp/... -count=10 -race -timeout 60mok945.8s, exit 0, 0 bind collisionstask testgo vet ./...was run separately fromtask testdeliberately:task test's formatter can swallow build failures, so a package whose tests don't compile would otherwise read as green.The three
task testfailures are unrelated to this diff —TestNewServer_ReadTimeoutConfiguredandTestSecurityHeaders(pkg/api, "no available runtime found") andTestCompositeProvider_RealProviders(pkg/secrets/keyring, no keyring). Confirmed pre-existing by checking outorigin/mainand running the same three there: all three fail identically.API Compatibility
v1beta1API.Test-helper only; no production code touched.
Does this introduce a user-facing change?
No.
Special notes for reviewers
(*Server).Start(point 2). If that ordering is ever inverted,Address()falls back to the configured port —0— and every test in this package fails loudly rather than flakily, so the regression could not go unnoticed.pkg/transport/proxy/streamablehas its owngetFreePort(streamable_proxy_integration_test.go:25) with the identical bind-close-rebind shape, used by 8 call sites. Separate package, separate flake surface, left untouched to keep this to one logical change. Filed separately.Generated with Claude Code