Skip to content

[Repo Assist] fix: use TcpListener(0) and StartAsync in XmlExtensions tests to avoid Windows CI failures#1774

Merged
dsyme merged 2 commits into
mainfrom
repo-assist/fix-windows-ci-port-binding-2026-05-08-f18a3441d94bbb4a
May 8, 2026
Merged

[Repo Assist] fix: use TcpListener(0) and StartAsync in XmlExtensions tests to avoid Windows CI failures#1774
dsyme merged 2 commits into
mainfrom
repo-assist/fix-windows-ci-port-binding-2026-05-08-f18a3441d94bbb4a

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented May 8, 2026

🤖 This is an automated pull request from Repo Assist, an AI assistant for this repository.

Root Cause

On Windows CI, XmlExtensions.fs tests were intermittently failing with:

System.Net.Sockets.SocketException (10013): An attempt was made to access a socket in a way forbidden by its access permissions.

This error (WSAEACCES) occurs when Kestrel tries to bind a port that is in a Windows excluded port range — ranges reserved by Hyper-V, Docker, or Windows services. These excluded ports are not visible via IPGlobalProperties.GetActiveTcpListeners(), so the previous random-port approach would pick such a port, consider it "free", and then fail at the Kestrel bind step.

Fix

Two changes to startXmlHttpLocalServer() in tests/FSharp.Data.Core.Tests/XmlExtensions.fs:

1. TcpListener(0) port selection (like Http.fs already does)

Before (flawed random approach):

let freePort =
    let random = new System.Random()
    let mutable port = random.Next(10000, 65000)
    while
        IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners()
        |> Array.map (fun x -> x.Port)
        |> Array.contains port do
            port <- random.Next(10000, 65000)
    port

After (OS-assigned port, never excluded):

let freePort =
    let listener = new TcpListener(System.Net.IPAddress.Loopback, 0)
    listener.Start()
    let port = (listener.LocalEndpoint :?> System.Net.IPEndPoint).Port
    listener.Stop()
    port

Asking the OS to bind port 0 lets the kernel pick a port from the dynamic range, guaranteeing it is not in any excluded range.

2. StartAsync() instead of RunAsync() + Thread.Sleep(100)

Before: app.RunAsync(baseAddress) is fire-and-forget; each test then called Thread.Sleep(100) hoping the server started.

After: app.StartAsync() returns only after the server is listening, making readiness deterministic and removing 13 Thread.Sleep(100) calls.

Evidence

The failure was observed in workflow run 25557443533 on the build-windows job. Http.fs already used TcpListener(0) and was not affected.

Test Status

Passed! - Failed: 0, Passed: 13, Skipped: 0, Total: 13, Duration: 1 s

Generated by 🌈 Repo Assist, see workflow run. Learn more.

Generated by 🌈 Repo Assist, see workflow run. Learn more.

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@fc4ab36dedc44e2a1cdc195cecce262f06c81230

…d Windows CI failures

On Windows CI, Hyper-V and Docker reserve ranges of ports that are not
visible via GetActiveTcpListeners(). The previous random-port approach
could pick a port in one of these excluded ranges, causing Kestrel to
fail with SocketException (10013: WSAEACCES) when binding.

Replace with the same TcpListener(0) approach already used in Http.fs:
ask the OS to bind port 0, read the assigned port (guaranteed not
excluded), then release the listener before Kestrel binds it.

Also switch from RunAsync (fire-and-forget) to StartAsync, which returns
only after the server is listening. This removes the need for
Thread.Sleep(100) in every test and makes server readiness deterministic.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@dsyme dsyme marked this pull request as ready for review May 8, 2026 13:45
@dsyme dsyme merged commit b99dfa7 into main May 8, 2026
3 checks passed
@dsyme dsyme deleted the repo-assist/fix-windows-ci-port-binding-2026-05-08-f18a3441d94bbb4a branch May 8, 2026 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant