The error
When calling app.listen(), Express throws:
Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _setupListenHandle] (node:net:1897:16)
at listenInCluster (node:net:1945:12)
at Server.listen (node:net:2037:7)
This means another process is already bound to the port your Express app is trying to use.
Instant fix: portrm
$ ptrm fix 3000
Port 3000 in use
node (PID 54321) - server.js
Killed PID 54321. Port 3000 is free.
$ node server.js # starts normally
Manual fix
# macOS / Linux
$ lsof -ti :3000 | xargs kill -9
# Windows
> netstat -ano | findstr :3000
> taskkill /PID <PID> /F
Handle EADDRINUSE in code
Instead of crashing, catch the error and show a helpful message:
const app = require('express')();
const PORT = process.env.PORT || 3000;
const server = app.listen(PORT, () => {
console.log(`Listening on port ${PORT}`);
});
server.on('error', (err) => {
if (err.code === 'EADDRINUSE') {
console.error(`Port ${PORT} is in use.`);
console.error('Run: ptrm fix ' + PORT);
process.exit(1);
}
throw err;
});
Add graceful shutdown
The most common cause of EADDRINUSE in Express is an orphaned process from a previous run. Add graceful shutdown to prevent this:
function shutdown() {
console.log('Shutting down gracefully...');
server.close(() => {
console.log('Server closed');
process.exit(0);
});
// Force exit after 5s if connections hang
setTimeout(() => process.exit(1), 5000);
}
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
Use a dynamic port fallback
Try the preferred port, then fall back to a random available port:
const net = require('net');
function getAvailablePort(preferred) {
return new Promise((resolve) => {
const srv = net.createServer();
srv.listen(preferred, () => {
srv.close(() => resolve(preferred));
});
srv.on('error', () => {
srv.listen(0, () => {
const port = srv.address().port;
srv.close(() => resolve(port));
});
});
});
}
getAvailablePort(3000).then((port) => {
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
});
nodemon / ts-node restart issues
When using nodemon, file saves trigger restarts. If the old process does not exit cleanly, the new one gets EADDRINUSE. Fix:
// nodemon.json
{
"signal": "SIGTERM",
"delay": 500
}
This gives the old process time to close the socket before the new one starts.
Stop debugging port conflicts
portrm finds and kills orphaned Express processes in one command. Add ptrm fix 3000 to your npm scripts.