OSError: [Errno 98] Address already in use
1 · The lesson
readWhat this error means
You tried to bind a server to a port that something else already holds. Only one process can listen on a given host and port at a time, so the second one is refused.
This is the listening side of the network. Its client-side counterpart is ConnectionRefusedError — nothing listening, versus something already listening.
When you see it
Traceback (most recent call last): File "server.py", line 9, in <module> app.run(port=8000) OSError: [Errno 98] Address already in use
On Windows:
OSError: [WinError 10048] Only one usage of each socket address is normally permitted
Why it happens
- A previous run is still going. The terminal was closed without stopping it, or it detached and kept running.
- Something else owns the port. 5000 on macOS is taken by AirPlay Receiver; 8080 is a favourite of every other tool.
- A dead process left the socket in
TIME_WAIT. The OS holds the address for up to a couple of minutes after a close so that late packets from the old connection are not delivered to a new one. - Two workers in one config both binding the same port, rather than the parent binding once and sharing it.
How to fix it
Find out what holds it:
lsof -iTCP:8000 -sTCP:LISTEN # macOS / Linux — shows the PID and command ss -ltnp | grep 8000 # Linux netstat -ano | findstr :8000 # Windows — last column is the PID
Then stop that process, or use another port. Read the command name before killing anything — on macOS, port 5000 is usually AirPlay rather than a stray server of yours.
For TIME_WAIT during development, allow reuse. This lets a restart bind immediately instead of waiting for the timer:
import socket s = socket.socket() s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # before bind, not after s.bind(("0.0.0.0", 8000)) s.listen()
Most frameworks already do this. Flask's development server and uvicorn set it for you, which is why you rarely hit TIME_WAIT with them and often do with a hand-rolled socket.
Take the port from configuration so a collision is a one-line change rather than an edit:
import os port = int(os.environ.get("PORT", "8000")) app.run(host="0.0.0.0", port=port)
setup added so this can run · defines app
import os # noqa: F401 os.environ.setdefault("PORT", "8000") # Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) app = _AutoMock('app')
Let the OS choose when you do not care — useful in tests, where a fixed port makes runs collide:
s.bind(("127.0.0.1", 0)) # 0 = any free port port = s.getsockname()[1] # ask which one you got
setup added so this can run · defines s
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) s = _AutoMock('s')
When you'd actually see this in real code
- A dev server backgrounded by an earlier command, still holding 8000 an hour later.
- Two test files each starting a server on the same fixed port, passing alone and failing together.
- A container restarting faster than the OS releases the socket, so the first attempt after a crash fails and the retry succeeds.
flask runon macOS port 5000, which is AirPlay and not your code at all.
Related errors
- [ConnectionRefusedError: [Errno 111] Connection refused](../error-connection-refused/) — the client-side mirror of this.
- [PermissionError: [Errno 13] Permission denied](../error-permissionerror/) — binding a port below 1024 without privileges.
See Also
- All Python errors — the full index, by type and by when it happens.
- Environment & Configuration
- DevOps & Deploy