The error, verbatim
Jump to the fix ↓node:events:486
throw er; // Unhandled 'error' event
^
Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2] (node:net:1948:16)
at listenInCluster (node:net:2005:12)
at Server.listen (node:net:2110:7)
Tested on
- Node
- 24.14.0
- OS
- Windows 11 Pro; Windows Server 2025 (GitHub Actions, November 2)
- Python
- 3.14.5
- Shells
- PowerShell 5.1, cmd
- kill-port
- 2.0.1
- Docker Desktop
- Engine 29.8.2 (WSL 2)
- Reserved ports
- re-checked November 7, 2026, after a restart (Node 24.14.0)
✓ Last re-run on GitHub Actions · see the run · all repros
Contents
- The fix01
- The same error from Python and other programs02
- Check what you’re about to stop03
- Or just use another port04
- One command instead of two05
- When netstat points at Docker or WSL06
- “Permission denied” instead: ports Windows reserves07
- No error, but the wrong app answers08
- Why it happens09
- What didn’t work10
- How this was tested11
The fix
Another process is already listening on the port. Find its process ID (PID), stop it, then start your server again.
PowerShell:
Get-NetTCPConnection -LocalPort 3000 -State Listen | Select-Object OwningProcess
Stop-Process -Id <PID> -ForceCommand Prompt:
netstat -ano | findstr :3000
taskkill /PID <PID> /FIn netstat, the PID is the last number on the LISTENING line:
TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 11156The same error from Python and other programs
Node calls it EADDRINUSE. Windows’ own wording, which Python passes straight through, is this (other languages word it their own way):
OSError: [WinError 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted
Same meaning, same fix. The commands above don’t care what language the program is written in; they ask Windows which process owns the port. I checked that with a Python server holding port 5055:
Get-NetTCPConnection -LocalPort 5055 -State Listen reported owning process 12500, and Get-Process -Id 12500 named it python. netstat agreed:
TCP 127.0.0.1:5055 0.0.0.0:0 LISTENING 12500
Stopping it freed the port immediately.
Check what you’re about to stop
The PID might be a dev server from another project that you still need. Look before you stop it:
Get-Process -Id <PID>
Or just use another port
Most dev servers accept a port flag or a PORT environment variable:
npx next dev -p 3001
npx vite --port 5174
For your own Node server, read the port from the environment so it’s easy to change:
const port = Number(process.env.PORT) || 3000;
server.listen(port);
One command instead of two
If you just want the port back and you’ve already checked what holds it:
npx kill-port 3000It stops whatever owns the port without showing you what that is, so look first (the section above).
When netstat points at Docker or WSL
If the PID belongs to com.docker.backend.exe or wslrelay.exe, a Docker container (or a server inside WSL) has published that port to Windows. I ran a container with -p 3020:5432 and started a Node server on 3020:
Error: listen EADDRINUSE: address already in use :::3020
TCP 0.0.0.0:3020 0.0.0.0:0 LISTENING 19044 ← com.docker.backend.exe
TCP [::1]:3020 [::]:0 LISTENING 12540 ← wslrelay.exe
Don’t kill those PIDs: they’re Docker Desktop and WSL themselves, not your app.
Find the container that publishes the port, and stop that:
docker ps --filter "publish=3020"
docker stop <container-name>“Permission denied” instead: ports Windows reserves
Sometimes the error on Windows 11 isn’t EADDRINUSE at all, and netstat shows nothing on the port:
Error: listen EACCES: permission denied 0.0.0.0:50100
Windows keeps blocks of ports reserved for itself, and features like Hyper-V, WSL and Docker can add more. Nothing is listening on those ports, so there’s no PID to stop. Python got through on a port that Node was refused on, so it depends on the program too. On my machine, port 80 and several ranges between 50060 and 53201 were reserved; 3000 wasn’t.
List the reserved ranges, then pick a port outside all of them:
netsh interface ipv4 show excludedportrange protocol=tcpNo error, but the wrong app answers
The opposite surprise: two programs on the same port, and no error at all. On Windows one app can listen on the IPv4 address 127.0.0.1:3000 while another listens on the IPv6 side (::, Node’s default), and both start fine. I ran a Python server on 127.0.0.1:3013 and a Node server on port 3013 at the same time:
$ curl http://localhost:3013/ → node app on ::
$ curl http://127.0.0.1:3013/ → python app on 127.0.0.1
localhost went to the Node app, so the browser showed one app while the other was still running unseen. netstat lists both, which is the clue:
TCP 0.0.0.0:3013 0.0.0.0:0 LISTENING 18512
TCP 127.0.0.1:3013 0.0.0.0:0 LISTENING 5564
TCP [::]:3013 [::]:0 LISTENING 18512
Two different PIDs on one port means two programs. Stop the one you don’t want with the commands at the top, or give one of them another port.
It can go further than the IPv4/IPv6 split. On November 2, on a Windows Server 2025 machine (GitHub’s windows-latest runner), Node and Python shared the same port with no error at all. With a Node server holding 0.0.0.0:3001 and [::]:3001, a Python socket bound both addresses without complaint, and with Python holding a port, Node started on it and printed that it was listening. Two Python servers on the same port did conflict, with the WinError 10048 above. So on Windows, a missing EADDRINUSE doesn’t prove the port was free: check netstat -ano for a second PID.
Why it happens
Only one process can listen on a given port at a time. The usual culprits:
- a dev server still running in another terminal tab or editor window
- a server from a crashed or closed session that’s still running in the background
- two projects that both default to port 3000
- a Docker container publishing the same port
What didn’t work
How this was tested
November 2, 2026: re-run on GitHub Actions windows-latest (Windows Server 2025, Node 24.22.1, Python 3.12.10). A second Node server on a busy port failed with EADDRINUSE; Get-NetTCPConnection and netstat -ano both named the right PID; Stop-Process and taskkill /F freed the port; npx kill-port printed Process on port 3001 killed; Node got EACCES on port 80, inside the machine’s excluded range; and localhost reached the Node app on :: while 127.0.0.1 reached the Python one. Docker wasn’t re-run. The script and output are public.
— N.K., end of entry No.003