EADDRINUSE: “address already in use” — explained and fixed

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.

Find what's holding the port

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.

Free the port

  1. Stop it properly if it's yours: the terminal running the old server — Ctrl+C. If that terminal is gone, kill by PID: Windows taskkill /PID <pid> /F; Mac/Linux kill <pid> (then kill -9 <pid> if it lingers).
  2. Or use a different port — often the better answer: most frameworks take PORT=3001 npm run dev / $env:PORT=3001 on Windows PowerShell. Two projects, two ports, no killing.
  3. Check for auto-restarters — nodemon, Docker containers, and services relaunch the process you just killed. Docker: docker ps and stop the container. Windows services: check the Services panel for your app.

Prevent the encore

  1. Stop dev servers with Ctrl+C, not by closing the terminal window — several toolchains leave the child process alive when the window dies.
  2. Give each project its own default port in its config/.env so parallel projects never collide.
  3. In code, fail kindly: log which port clashed and exit, rather than retry-looping silently — future-you reads logs.
Don't kill a process you don't recognise to free a port. If the holder is a database, a system service, or another team's app, the right move is choosing a different port for your app — the port was “already in use” because it's genuinely in use.

If it still happens

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.

Common questions

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.”

Related errors

Could not create the Java VM · npm ERESOLVE · Failed to push some refs