Description
On macOS 26.5.2 (Darwin 25.5.0), when an app is launched via the apphost (dotnet run, a
JetBrains Rider "Run" configuration, or executing the built apphost binary directly — i.e. not
via dotnet <path-to-dll> through the shared framework host), incoming connections that target a
secondary loopback alias (an address added to lo0 via e.g. sudo ifconfig lo0 alias 10.100.100.101) are broken. Genuine 127.0.0.1 / ::1 always work fine.
The exact symptom depends on how the Kestrel listener is bound:
| Kestrel binding |
127.0.0.1 |
Secondary loopback alias (e.g. 10.100.100.101) |
options.ListenAnyIP(5000) (dual-stack IPv6Any + DualMode) |
OK |
Process crashes on accept |
options.Listen(IPAddress.Any, 5000) (IPv4-only) |
OK |
Connection silently dropped, no crash |
Neither symptom reproduces via a raw dotnet <path-to-dll> exec (through the shared framework
host) — only via the apphost. Neither reproduces on .NET 8, on the same machine and launch method.
Full write-up, exact reproduction matrix, and a minimal repro project:
https://github.com/pieter-venter-wd/kestrel-macos-bug
Reproduction steps
sudo ifconfig lo0 alias 10.100.100.101
- Clone https://github.com/pieter-venter-wd/kestrel-macos-bug, open
KestrelIssue.sln
cd KestrelIssue && dotnet run
- In another terminal:
curl -v http://10.100.100.101:5000/weatherforecast
- Observe the process crash (with the default
options.ListenAnyIP(5000) binding) or an empty
reply with the process still alive (if switched to options.Listen(IPAddress.Any, 5000))
Compare against:
curl -v http://127.0.0.1:5000/weatherforecast — works fine, either binding
- Running
dotnet bin/Debug/net10.0/KestrelIssue.dll directly instead of dotnet run, then
repeating step 4 against the alias — works fine, either binding
- Switching
TargetFramework to net8.0 and repeating steps 3–4 — works fine, either binding
Expected behavior
Connections to a secondary loopback alias should behave the same as connections to 127.0.0.1.
Actual behavior
With a dual-stack ListenAnyIP binding, accepting the connection throws an unhandled exception
on a ThreadPool worker thread inside the socket accept completion callback, crashing the process:
System.ArgumentException: The supplied System.Net.SocketAddress is an invalid size for the
System.Net.IPEndPoint end point. (Parameter 'socketAddress')
at System.Net.IPEndPoint.Create(SocketAddress socketAddress)
at System.Net.Sockets.SocketAsyncEventArgs.FinishOperationSyncSuccess(Int32 bytesTransferred, SocketFlags flags)
at System.Net.Sockets.SocketAsyncEventArgs.FinishOperationAsyncSuccess(Int32 bytesTransferred, SocketFlags flags)
at System.Net.Sockets.SocketAsyncEventArgs.AcceptCompletionCallback(IntPtr acceptedFileDescriptor, Memory`1 socketAddress, SocketError socketError)
at System.Net.Sockets.SocketAsyncEngine.System.Threading.IThreadPoolWorkItem.Execute()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
at System.Threading.Thread.StartCallback()
With an IPv4-only Listen(IPAddress.Any, ...) binding instead, the process does not crash, but the
connection is silently dropped: the TCP handshake completes, the HTTP request is sent and received,
and then the connection closes immediately with zero response bytes (curl: (52) Empty reply from server, within tens of milliseconds — not a timeout). Nothing is logged even with
Microsoft.AspNetCore.Server.Kestrel set to Debug/Trace.
Regression?
Yes — does not reproduce on .NET 8 with the same OS, machine, and launch method.
Known workarounds
- None found for full connectivity. Targeting
net8.0 instead of net10.0 avoids both symptoms
entirely.
- Binding IPv4-only avoids the crash but does not restore connectivity — it only trades the crash
for a silent connection drop.
Configuration
- .NET SDK 10.0.301 / runtime 10.0.9
- macOS 26.5.2 (Darwin 25.5.0), Apple Silicon (arm64)
Other information
Reproduced with both the .NET 10 SDK's default minimal-API project template
(Microsoft.AspNetCore.OpenApi) and a stripped-down Kestrel-only app with zero extra
dependencies — not specific to any package.
Description
On macOS 26.5.2 (Darwin 25.5.0), when an app is launched via the apphost (
dotnet run, aJetBrains Rider "Run" configuration, or executing the built apphost binary directly — i.e. not
via
dotnet <path-to-dll>through the shared framework host), incoming connections that target asecondary loopback alias (an address added to
lo0via e.g.sudo ifconfig lo0 alias 10.100.100.101) are broken. Genuine127.0.0.1/::1always work fine.The exact symptom depends on how the Kestrel listener is bound:
127.0.0.110.100.100.101)options.ListenAnyIP(5000)(dual-stackIPv6Any+DualMode)options.Listen(IPAddress.Any, 5000)(IPv4-only)Neither symptom reproduces via a raw
dotnet <path-to-dll>exec (through the shared frameworkhost) — only via the apphost. Neither reproduces on .NET 8, on the same machine and launch method.
Full write-up, exact reproduction matrix, and a minimal repro project:
https://github.com/pieter-venter-wd/kestrel-macos-bug
Reproduction steps
sudo ifconfig lo0 alias 10.100.100.101KestrelIssue.slncd KestrelIssue && dotnet runcurl -v http://10.100.100.101:5000/weatherforecastoptions.ListenAnyIP(5000)binding) or an emptyreply with the process still alive (if switched to
options.Listen(IPAddress.Any, 5000))Compare against:
curl -v http://127.0.0.1:5000/weatherforecast— works fine, either bindingdotnet bin/Debug/net10.0/KestrelIssue.dlldirectly instead ofdotnet run, thenrepeating step 4 against the alias — works fine, either binding
TargetFrameworktonet8.0and repeating steps 3–4 — works fine, either bindingExpected behavior
Connections to a secondary loopback alias should behave the same as connections to
127.0.0.1.Actual behavior
With a dual-stack
ListenAnyIPbinding, accepting the connection throws an unhandled exceptionon a ThreadPool worker thread inside the socket accept completion callback, crashing the process:
System.ArgumentException: The supplied System.Net.SocketAddress is an invalid size for the
System.Net.IPEndPoint end point. (Parameter 'socketAddress')
at System.Net.IPEndPoint.Create(SocketAddress socketAddress)
at System.Net.Sockets.SocketAsyncEventArgs.FinishOperationSyncSuccess(Int32 bytesTransferred, SocketFlags flags)
at System.Net.Sockets.SocketAsyncEventArgs.FinishOperationAsyncSuccess(Int32 bytesTransferred, SocketFlags flags)
at System.Net.Sockets.SocketAsyncEventArgs.AcceptCompletionCallback(IntPtr acceptedFileDescriptor, Memory`1 socketAddress, SocketError socketError)
at System.Net.Sockets.SocketAsyncEngine.System.Threading.IThreadPoolWorkItem.Execute()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
at System.Threading.PortableThreadPool.WorkerThread.WorkerThreadStart()
at System.Threading.Thread.StartCallback()
With an IPv4-only
Listen(IPAddress.Any, ...)binding instead, the process does not crash, but theconnection is silently dropped: the TCP handshake completes, the HTTP request is sent and received,
and then the connection closes immediately with zero response bytes (
curl: (52) Empty reply from server, within tens of milliseconds — not a timeout). Nothing is logged even withMicrosoft.AspNetCore.Server.Kestrelset toDebug/Trace.Regression?
Yes — does not reproduce on .NET 8 with the same OS, machine, and launch method.
Known workarounds
net8.0instead ofnet10.0avoids both symptomsentirely.
for a silent connection drop.
Configuration
Other information
Reproduced with both the .NET 10 SDK's default minimal-API project template
(
Microsoft.AspNetCore.OpenApi) and a stripped-down Kestrel-only app with zero extradependencies — not specific to any package.