socket.gaierror: [Errno -2] Name or service not known
1 · The lesson
readWhat this error means
DNS could not turn the hostname into an IP address. No connection was ever attempted, because there was no address to connect to. gaierror is short for getaddrinfo error — the failure is in the name lookup itself, before any networking happens.
When you see it
Traceback (most recent call last): File "sync.py", line 8, in <module> r = requests.get("https://api.internl.com/v1/users") socket.gaierror: [Errno -2] Name or service not known
Through requests, wrapped:
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='db', port=5432): Max retries exceeded (Caused by NewConnectionError( 'Failed to establish a new connection: [Errno -2] Name or service not known'))
Why it happens
- A typo in the hostname.
api.internl.comabove is missing ana. - The name only exists on a network you are not on. An internal service name resolves inside the VPN or the cluster and nowhere else.
- A Docker Compose service name used outside that network —
dbresolves from a sibling container and means nothing on your laptop. - A hostname built from an empty variable.
f"https://{host}/v1"withhostunset becomeshttps:///v1, and an empty name cannot resolve. - DNS itself is broken in the container — no resolver configured, or an empty
/etc/resolv.conf.
How to fix it
Resolve it by hand first. This separates "my code is wrong" from "the network is wrong":
getent hosts api.example.com # Linux dig +short api.example.com nslookup api.example.com # Windows
Nothing back means the name is wrong or DNS is unreachable, and no amount of Python will fix it.
Check the value you actually passed — the commonest cause and the easiest to miss:
import os host = os.environ.get("API_HOST") if not host: raise SystemExit("API_HOST is not set — there is nothing to connect to") print(f"connecting to {host!r}") # !r makes '' and stray whitespace visible
setup added so this can run · defines
import os # noqa: F401 os.environ.setdefault("API_HOST", "example-api-host")
The !r matters. An empty string or a trailing newline both cause this error and both look fine without it.
Inside Docker, use the service name from the same network:
services:
api:
environment:
DB_HOST: db # resolves on the compose network
depends_on: [db]
db:
image: postgres:16Say what failed when it is configuration rather than a bug:
import socket import requests try: r = requests.get(url, timeout=(3.05, 10)) except requests.exceptions.ConnectionError as exc: if isinstance(exc.__cause__, socket.gaierror): raise SystemExit(f"Cannot resolve {url!r} — check the hostname and your DNS") raise
setup added so this can run · defines url
# 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,) url = _AutoMock('url')
When you'd actually see this in real code
- A
.envcopied between machines, still pointing at an internal hostname that only resolves on the office VPN. - A deploy where a service was renamed in Compose but one environment variable still names the old one.
- A variable that is set but empty, so the URL has no host at all. This is the one people stare at longest, because the config "looks fine".
- A scheduled container without DNS configured, where the same code works interactively and fails on the timer.
Related errors
- [ConnectionRefusedError: [Errno 111] Connection refused](../error-connection-refused/) — the name resolved; nothing was listening.
- TimeoutError / requests.exceptions.ReadTimeout — resolved and connected, then silence.
See Also
- All Python errors — the full index, by type and by when it happens.
- Environment & Configuration
- APIs & HTTP