EADDRINUSE means a program (Node most often) tried to start listening on a port that another process already owns. One port, one listener — the second one loses. The other process is usually your own app from an earlier run that never actually stopped: a terminal closed without killing it, a crashed watcher, a forgotten dev server.
Windows (Command Prompt or PowerShell):
netstat -ano | findstr :3000 tasklist | findstr <PID>
Mac / Linux:
lsof -i :3000
Both name the process and its PID. Nine times out of ten it's node — your own previous server.
taskkill /PID <pid> /F; Mac/Linux kill <pid> (then kill -9 <pid> if it lingers).PORT=3001 npm run dev / $env:PORT=3001 on Windows PowerShell. Two projects, two ports, no killing.docker ps and stop the container. Windows services: check the Services panel for your app.IPv6/IPv4 double-binding trips some setups (the message shows :::3000): an app bound to IPv6-any can clash with one bound to IPv4 on the same number. Binding explicitly to 127.0.0.1 in dev removes the ambiguity. On Windows, also check for the port being in an excluded/reserved range (netsh interface ipv4 show excludedportrange protocol=tcp) — Hyper-V and WSL reserve blocks that produce this error with nothing visibly listening.
Why does it say EADDRINUSE when I stopped the server? The stop didn't reach the actual process — closed terminal, crashed parent, or a watcher respawned it. The PID lookup shows the survivor; it's real, not a ghost in the OS.
Can two apps share a port with different addresses? Yes — binding different specific addresses (127.0.0.1 vs a LAN IP) can coexist. The clash happens when both want the same address+port, or one wants “all addresses.”