ErrorNode.js

Port already in use (EADDRINUSE)

What EADDRINUSE means when Node's server can't bind a port, the most likely reasons it's already taken, and how to free it or pick another one.

Last updated

ErrorNode.js
Error: listen EADDRINUSE: address already in use :::3000
    at Server.setupListenHandle [as _listen2] (node:net:1900:16)

What it means: Node tried to bind a server to a TCP port that's already held by another process, so the listen() call failed outright.

Likely causes

Most probable first — with how to confirm.

  • 1.Another instance of your own app (a previous run, a crashed process, or a second `npm run dev`) is still bound to the port — check with `lsof -i :3000` (macOS/Linux) or `netstat -ano | findstr :3000` (Windows) to see the PID.
  • 2.A different service (another dev server, a database like Postgres on 5432, or a proxy) happens to be using the same port — check the process name behind that PID before killing anything.
  • 3.The previous process is still alive — an orphaned child, a detached watcher, or a debugger holding the socket. On Linux and macOS, Node sets SO_REUSEADDR, so a lingering TIME_WAIT socket does NOT normally block a rebind; check with `lsof -i tcp:3000 -sTCP:LISTEN` before assuming it's a timing problem. (Windows is the exception, where the semantics differ.)
  • 4.The port is hardcoded and something else on the machine already owns it permanently, rather than a one-off collision.

Fixes

Safest first; destructive ones are called out.

  • →Kill the LISTENER specifically: `kill $(lsof -t -i tcp:3000 -sTCP:LISTEN)` on macOS/Linux. The `-sTCP:LISTEN` matters — a bare `lsof -i:3000` also matches every client connected TO that port, so piping it into kill can take down your browser or an unrelated service. On Windows, find the PID via `netstat -ano | findstr :3000` and `taskkill /PID <pid>`. Escalate to `kill -9` only if a normal kill doesn't land.
  • →Change the port your app listens on (an env var like `PORT=3001`, or the framework's config) if you don't control what else is running on the machine.
  • →If it's your own dev server restarting too fast, give the watcher a moment to fully stop the old process before starting a new one.

Part by part

listen
The syscall that failed. It tells you the process was trying to accept connections, not make one — which rules out a whole class of client-side causes.
EADDRINUSE
The POSIX error code: another socket is already bound to this address and port. It comes from the operating system, not from Node.
:::3000
The address, in IPv6 shorthand. The three colons are the IPv6 wildcard followed by the port, meaning it tried to listen on every interface.

Common mistakes

  • kill -9 $(lsof -t -i:3000)

    Without a LISTEN filter this matches every process holding a connection to that port, including a browser tab you have open against it — so it can force-kill your own browser. The -9 also denies the server any chance to release resources.

    kill $(lsof -t -i tcp:3000 -sTCP:LISTEN)
  • Changing the port and moving on

    It gets you running and leaves an orphaned process holding the original port, which reappears tomorrow. Worth doing under time pressure, worth coming back to afterwards.

  • sudo lsof -i:3000 && sudo kill -9 <pid>

    Escalating to root is rarely necessary for a process you started, and it makes it possible to kill a system service that legitimately owns the port. Try without sudo first and read what comes back.

Questions

Why does the port stay busy after I stop the server?

Usually TIME_WAIT: the kernel holds the socket for a minute or two to catch stray packets from the closed connection. It clears on its own, and a server that sets SO_REUSEADDR can rebind immediately regardless.

What if nothing is listening on that port?

Then something else is binding it — commonly a second copy of your own app started by a file watcher, or a container publishing the same port. Check docker ps as well as lsof before concluding the port is free.

Why does it say :::3000 rather than a real address?

That is the IPv6 unspecified address, meaning it asked to listen on every interface. If you only need local access, binding explicitly to 127.0.0.1 both avoids some conflicts and stops the server being reachable from your network.

How do I stop this recurring in development?

Make the dev server shut down cleanly on SIGINT and SIGTERM, so the socket is released when you stop it. Most orphaned listeners come from a process that was killed abruptly rather than asked to exit.

Related in Errors