← Debugging Guides

阅读中文版本

Node.js — server.listen EADDRINUSE in tests or watch mode

Fix EADDRINUSE in tests and watch mode by releasing ports, using port 0, and closing servers after each test.

intermediatedebuggingnodetesting

Symptoms

  • Error: listen EADDRINUSE: address already in use :::3000
  • Tests pass individually but fail when run together
  • Watch mode restarts before the old server released the port
  • A random port still collides occasionally

Possible causes

  • A previous server instance still listens on the fixed port
  • Tests start a server but never close it
  • The test runner parallelizes tests that all bind the same port
  • Watch mode restarts faster than the socket is released

Diagnosis steps

Find the lingering listener

ss -ltnp | grep :3000

Interpretation

The process holding the port is often a leftover test server or an earlier watch process.

Use an ephemeral port in tests

const server = app.listen(0); const { port } = server.address()

Interpretation

Binding to port 0 lets the OS pick a free port, avoiding collisions between parallel tests.

Close servers in teardown

afterAll(() => server.close())

Interpretation

Releasing the listener after each suite frees the port for the next run.

Root cause

The port is already bound, either by a leftover server, by parallel tests sharing a fixed port, or by a previous instance not yet released during a restart.

Fix

Close servers in teardown, bind to port 0 in tests so the OS chooses a free port, and give watch mode time to release the socket or randomize its port.

Verify the fix

Run the full test suite and confirm no EADDRINUSE, and confirm the port is free after the run with a listener check.

Symptom walkthrough

Starting a server fails with EADDRINUSE, and tests that pass individually fail when run together. The pattern points at port reuse: a leftover server, parallel tests binding one fixed port, or watch mode restarting before the previous socket is released. Binding to a hardcoded port makes every run compete for the same resource.

Fix in practice

Close servers in teardown so ports are freed, bind to port 0 in tests and read the assigned port from the server address, and avoid fixed ports in watch mode. These changes let runs coexist without contending for one port.

Verification

Run the full suite and confirm no EADDRINUSE, then check that the port is free afterwards. Running clean together and leaving no listener behind confirms the fix. A fixture that both starts and stops the server also prevents future collisions between suites.

Diagnostic commands

Check the behavior with commands like these:

ss -ltnp | grep :3000
const server = app.listen(0); const { port } = server.address()
afterAll(() => server.close())