How to Fix EADDRINUSE in Node.js
Error: listen EADDRINUSE means the port is already taken. Here's how to find the process on Mac, kill it, change your port, and stop the error from coming back.
You run node server.js and Node throws:
Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2] (node:net:...)
It means exactly what it says: another process is already listening on the port your app wants. Here’s the fast fix and how to stop it recurring.
What EADDRINUSE means
When your Node server calls app.listen(3000), it asks the OS to bind to port 3000. If something already holds that port, the bind fails and Node surfaces the system error EADDRINUSE (“address already in use”). The :::3000 in the message is the IPv6 form of “port 3000 on all interfaces.”
Nine times out of ten in development, the culprit is a previous instance of your own server that didn’t shut down, a crashed process, a stuck nodemon, or a terminal you closed without stopping the server.
Step 1: Find what’s on the port
On a Mac:
lsof -i :3000 -n -P
COMMAND PID USER ... NODE NAME
node 1421 aaron ... TCP *:3000 (LISTEN)
There it is, an orphaned node process, PID 1421, still squatting on 3000.
Step 2: Kill it
The one-liner that finds and kills whatever holds the port:
kill -9 $(lsof -ti :3000)
lsof -ti :3000 prints just the PID; kill -9 force-terminates it. Now port 3000 is free and node server.js will start.
For a cleaner shutdown that lets the process release resources first, try a plain kill (SIGTERM) before resorting to -9 (SIGKILL):
kill $(lsof -ti :3000)
More on the SIGTERM vs SIGKILL choice in kill a process by port on Mac.
Step 3 (alternative): Run on a different port
If you don’t want to hunt down the other process, just move your app:
PORT=3001 node server.js
This works when your code reads the port from the environment, which it should:
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on ${port}`));
Stop EADDRINUSE from coming back
A few habits eliminate most repeat offenders:
Handle the error instead of crashing blind. Catch it and print something useful:
const server = app.listen(port);
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error(`Port ${port} is already in use. Stop the other process or set a different PORT.`);
process.exit(1);
} else {
throw err;
}
});
Shut down cleanly on Ctrl-C so the port is released when you stop the server:
process.on('SIGINT', () => {
server.close(() => process.exit(0));
});
Use a process manager like nodemon or pm2 that restarts your app and releases the old port between runs, instead of leaving orphans behind.
Why it happens at all
The error is just Node reporting the universal rule that only one program can listen on a port at once. EADDRINUSE is the Node.js face of the same underlying condition behind the shell’s bind: address already in use, same cause, same fixes.
See the orphaned process instantly
Portie shows every process holding a port on your Mac in a live table. When Node throws EADDRINUSE, you can see the leftover server sitting on port 3000 and end it with one click, no lsof incantation required.
Local monitoring is free. The $8.99 one-time unlock adds one-click process termination and remote port scanning. Download Portie and kill orphaned servers the moment they appear.