Getting hit with STATUS_ADDRESS_ALREADY_ASSOCIATED (0xC0000238) is annoying because your app looks fine — the code compiled, the config seems right, and then Windows just says no. Here's the fix, no fluff.
The Straight Fix
- Open Command Prompt as Administrator (right-click Run as administrator).
- Run this command to find the process hogging your port — replace
8080with whatever port your app uses:
Look for the PID in the last column. That's the process identifier.netstat -ano | findstr :8080 - Kill it:
Thetaskkill /PID <the_pid> /F/Fforces termination. - Restart your application. The error should be gone.
If the process keeps coming back — like a Windows service set to auto-restart — go to Services (services.msc), find the corresponding service, and stop it there. Or change its startup type to Disabled if you don't need it.
Why This Happens
What's actually happening here is that the Winsock API forbids binding a socket to an address+port pair that's already in use by another socket on the same protocol (TCP or UDP). The OS kernel keeps a table of active bindings. When your app calls bind() with SOCK_STREAM on 0.0.0.0:8080, the kernel checks if any existing entry in its bind table matches. If there's a match — even from a zombie process that crashed without a proper closesocket() — you get 0xC0000238.
This is a kernel-level restriction, not something you can override with SO_REUSEADDR in every case. SO_REUSEADDR only lets you bind multiple sockets to the same port if they're all listening on different multicast addresses or using SO_EXCLUSIVEADDRUSE isn't set. It doesn't let you steal a port from a live listening TCP socket. The kernel blocks that deliberately — otherwise two apps could receive each other's traffic, which breaks TCP's reliability guarantees.
The specific real-world trigger I see most on Windows 10 22H2 and Windows 11 23H2: a Node.js dev server (like Vite or Webpack Dev Server) crashes during a hot reload, but the child worker process keeps the TCP port open. Next time you restart, bind() fails. Or a Python Flask app that was killed with Ctrl+C but the socket stays in TIME_WAIT state for 2–4 minutes.
Less Common Variations
WSL / Docker Edge Case
If you're running WSL2 or Docker Desktop, the port might be bound on the Windows host side by vpnkit or wslrelay. Run netstat -ano | findstr :8080 from Windows. If the PID belongs to a system process (PID 4), it's the TCP/IP stack redirecting to WSL. Kill the WSL instance with wsl --shutdown and restart Docker. That usually clears the ghost binding.
SO_REUSEADDR on UDP
For UDP multicast receivers, the fix is different. If you see 0xC0000238 on a UDP socket, check that every socket using the same port is using SO_REUSEADDR before the bind() call. Winsock requires all sockets sharing a UDP port to set this flag, not just the second one. The usual culprit is one socket missing the setsockopt() call.
Windows Filtering Platform (WFP)
Rare, but I've seen third-party firewalls (looking at you, Comodo, and some corporate VPN clients) hold a port open via WFP callout drivers. The socket won't show up in netstat at all. Run netsh wfp show state — if you see your port listed under ale_layers, disable the filter driver (uninstall the firewall/VPN) and test.
Prevention
Don't rely on SO_REUSEADDR as a crutch. The real fix is:
- Always call
closesocket()on error paths — especially after a failedconnect()or beforeexit(). Usetry/finallyblocks in C++,try/except finallyin Python, or theusingstatement in C#. - Set
SO_EXCLUSIVEADDRUSE(Windows-specific). This prevents any other socket from binding to your port while your app is alive. It doesn't stop theTIME_WAITissue, but it does prevent accidental double-bind by another instance of your app. - Pick ephemeral ports for dev servers — let the OS assign one. In Node.js, use
port: 0inserver.listen(). In Python, bind toport=0and read backs.getsockname()[1]. This sidesteps the whole collision problem during development. - Use a proper shutdown sequence. If your app is a Windows service, implement
SERVICE_ACCEPT_PRESHUTDOWNso it gets 120 seconds to close sockets gracefully instead of being killed instantly.
That's it. Kill the ghost process, set the right socket options, write cleanup code, and you won't see 0xC0000238 again.