EADDRINUSE: the error explained
EADDRINUSE stands for Error: Address Already In Use. It is a POSIX error code (errno 48 on macOS, 98 on Linux) that the operating system returns when a process calls bind() on a TCP socket, but another process already holds that address+port combination.
The full error message in Node.js looks like this:
Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _setupListenHandle] (net.js:1331:16)
at listenInCluster (net.js:1379:12)
at Server.listen (net.js:1465:7)
The :::3000 part means port 3000 on all IPv6 (and IPv4) interfaces. The triple colon is IPv6 notation for "all addresses."
How TCP port binding works
When a server application starts, it goes through these steps:
- socket() - Create a new TCP socket
- bind() - Associate the socket with a specific IP address and port number
- listen() - Start accepting incoming connections
The operating system enforces a rule: only one process can bind() to a given address+port at a time. If another process is already bound to 0.0.0.0:3000, any new bind() call for the same port fails with EADDRINUSE.
Common causes of EADDRINUSE
1. A previous server instance is still running
You started a dev server, closed the terminal, but the process is still alive in the background. This is the most common cause.
2. Hot reload created a duplicate
Tools like nodemon or Next.js hot reload sometimes spawn a new server process without killing the old one, leaving both bound to the same port.
3. Multiple projects on the same port
Two different projects both default to port 3000. React, Next.js, Express, and Rails all use 3000 by default.
4. TIME_WAIT state
After a TCP connection closes, the OS keeps the socket in TIME_WAIT for 30-120 seconds to handle late-arriving packets. During this window, no new process can bind to that port.
5. System service using the port
On macOS Monterey+, AirPlay Receiver uses port 5000. On some Linux distros, Avahi uses port 5353. These system services silently occupy common development ports.
How to fix EADDRINUSE
Quick fix with portrm
$ ptrm fix 3000
Port 3000 in use
Next.js (PID 48291) - node
SAFE - dev server, safe to kill
Killed PID 48291. Port 3000 is free.
Manual fix on macOS/Linux
$ lsof -i :3000
$ kill -9 <PID>
Manual fix on Windows
> netstat -ano | findstr :3000
> taskkill /PID <PID> /F
Prevent EADDRINUSE in your code
Handle the error gracefully
// Node.js: catch EADDRINUSE and suggest a fix
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error(`Port ${port} is in use.`);
console.error(`Run: ptrm fix ${port}`);
process.exit(1);
}
});
Use SO_REUSEADDR
The SO_REUSEADDR socket option lets a new process bind to a port that is in TIME_WAIT state. Node.js enables this by default for server.listen(), but not all languages do.
Use portrm preflight
$ ptrm preflight 3000 && npm run dev
This checks if port 3000 is free before starting your server. If it is occupied, it exits with a helpful message showing the conflicting process.
Fix EADDRINUSE in one command
portrm identifies the process, checks safety, and frees the port. No PID hunting required.