"EADDRINUSE: address already in use" means Node.js cannot bind to a network port because another process has already claimed it.
What "EADDRINUSE: address already in use" Means
This is a Node.js runtime error, not a browser or TypeScript compiler issue. When your server calls listen(), the operating system checks if that port is free. If another process holds it, Node.js throws EADDRINUSE and your app crashes. The error message includes the port number, like EADDRINUSE: address already in use :::3000.
Why It Happens
Three causes cover 95% of cases:
- A previous server instance is still running. You Ctrl+C'd a terminal but the process didn't die, or you're running the app from multiple terminals.
- Another service owns the port. PostgreSQL uses 5432, Redis uses 6379, and many dev tools grab 3000 or 8080 by default.
- The port is in
TIME_WAITstate. After a server closes, the OS holds the socket for a short period. On Linux, this typically lasts 60 seconds.
Example Code That Triggers It
// server.js
const http = require('http');
const server = http.createServer((req, res) => {
res.end('Hello from port 3000');
});
server.listen(3000, () => {
console.log('Server listening on http://localhost:3000');
});
Run this twice in two terminals: node server.js. The second run throws Error: listen EADDRINUSE: address already in use :::3000 immediately.
How to Fix It
The cleanest fix is to make the port configurable and fail gracefully:
const http = require('http');
const port = process.env.PORT || 3000;
const server = http.createServer((req, res) => {
res.end('Hello from port ' + port);
});
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error(`Port ${port} is in use. Try: PORT=3001 node server.js`);
process.exit(1);
}
throw err;
});
server.listen(port, () => {
console.log(`Server listening on http://localhost:${port}`);
});
The error event handler catches EADDRINUSE before it crashes the process, gives you a clear message, and exits cleanly. Setting PORT via environment variable lets you switch ports without editing code.
Common Mistakes That Cause This
Mistake 1: Killing the terminal but not the process. On macOS/Linux, Ctrl+C sends SIGINT, but if your app has open connections, it might ignore it. Use lsof -i :3000 to find the PID, then kill -9 <PID>.
Mistake 2: Hardcoding ports in multiple files. When you hardcode 3000 in server.js and also in a config file, you'll end up with two processes fighting over the same port. Centralize port config in one place — a .env file or a single config module.
When Should You Worry About This?
If you're in production and see EADDRINUSE, that's a real deployment problem — a previous instance didn't shut down properly, or your process manager (PM2, systemd) restarted the app while the old one was still draining connections. In development, it's almost always a leftover process. But if it happens consistently on different ports, your code might be calling listen() in a loop or inside a hot-reload callback without closing the old server.
First thing to check: run lsof -i :<port> (macOS/Linux) or netstat -ano | findstr :<port> (Windows) to see exactly which PID owns the port — then decide whether to kill it or change your port.