PythonMastery
reference 3 min read · lesson 40 of 45 in Errors

OSError: [Errno 98] Address already in use

1 · The lesson

read

What 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

python
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:

python
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:

bash
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:

python
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:

python
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:

python
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 run on macOS port 5000, which is AirPlay and not your code at all.
  • [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