OSError: [Errno 113] No route to host in Python (tested)

OSError: [Errno 113] No route to host (EHOSTUNREACH) means the kernel could not reach the host at all. We reproduced both common causes: an address nobody answers (fails after about 3 seconds) and a firewall REJECT rule on a server that is up (fails instantly). It is a plain OSError, not a ConnectionError.

1. Reproduce it: connect to an address nobody answers

import socket, time

ip = socket.gethostbyname(socket.gethostname())          # this container's address
target = ip.rsplit(".", 1)[0] + ".250"                   # an unused address on the same subnet
t = time.monotonic()
try:
    socket.create_connection((target, 80), timeout=10)
except OSError as e:
    print(f"{type(e).__name__}: {e}  (errno {e.errno}, after {time.monotonic() - t:.1f}s)")

Output:

OSError: [Errno 113] No route to host  (errno 113, after 3.1s)

The container asked its subnet for .250 (an unused address). Nobody answered the ARP request, so after about 3 seconds the kernel gave up with EHOSTUNREACH. The same code on Alpine (musl libc) prints different text for the same errno:

OSError: [Errno 113] Host is unreachable  (errno 113, after 3.1s)

So [Errno 113] No route to host and [Errno 113] Host is unreachable are the same error.

2. The surprising cause: a firewall REJECT rule

import socket, subprocess, time

# firewall on the server side: REJECT with icmp-host-prohibited (firewalld's default reject)
subprocess.run(["iptables", "-A", "INPUT", "-p", "tcp", "--dport", "8080",
                "-j", "REJECT", "--reject-with", "icmp-host-prohibited"], check=True)
srv = socket.create_server(("0.0.0.0", 8080))           # the service IS listening
ip = socket.gethostbyname(socket.gethostname())
t = time.monotonic()
try:
    socket.create_connection((ip, 8080), timeout=10)
except OSError as e:
    print(f"{type(e).__name__}: {e}  (after {time.monotonic() - t:.2f}s, server is up and listening)")

Output:

OSError: [Errno 113] No route to host  (after 0.00s, server is up and listening)

The server was up and listening, yet the client got No route to host instantly. A firewall rule that rejects with icmp-host-prohibited (firewalld's default reject) produces exactly this errno. If the host answers ping but a port gives errno 113, check the server's firewall (firewall-cmd --list-all, iptables -L -n) and open the port, rather than debugging routing.

3. It is a plain OSError, not ConnectionError

import errno, socket

target = socket.gethostbyname(socket.gethostname()).rsplit(".", 1)[0] + ".250"
try:
    socket.create_connection((target, 80), timeout=10)
except OSError as e:
    print("errno name:", errno.errorcode[e.errno], "| EHOSTUNREACH =", errno.EHOSTUNREACH)
    print("ConnectionError?", isinstance(e, ConnectionError), "| TimeoutError?", isinstance(e, TimeoutError))
    print("class:", type(e).__name__)

Output:

errno name: EHOSTUNREACH | EHOSTUNREACH = 113
ConnectionError? False | TimeoutError? False
class: OSError

Python maps errno 111 to ConnectionRefusedError, but errno 113 has no subclass: except ConnectionError and except TimeoutError both miss it. Catch OSError and compare e.errno == errno.EHOSTUNREACH.

4. The same error through requests

import socket, requests

target = socket.gethostbyname(socket.gethostname()).rsplit(".", 1)[0] + ".250"
try:
    requests.get(f"http://{target}/", timeout=10)
except requests.exceptions.ConnectionError as e:
    print(type(e).__name__ + ":", e)

Output:

ConnectionError: HTTPConnectionPool(host='172.17.0.250', port=80): Max retries exceeded with url: / (Caused by NewConnectionError("HTTPConnection(host='172.17.0.250', port=80): Failed to establish a new connection: [Errno 113] No route to host"))

requests wraps it in requests.exceptions.ConnectionError with Max retries exceeded; the [Errno 113] at the end is the real cause.

5. Checklist

Related: [Errno 101] Network is unreachable · [Errno 111] Connection refused · socket errors and errno codes.

Frequently asked questions

What does [Errno 113] No route to host mean?

EHOSTUNREACH: the host did not answer at the network level (wrong IP, host down) or a firewall rejected the connection with an ICMP host-prohibited reply.

Why do I get No route to host when the server is running?

A firewall REJECT rule (firewalld rejects with icmp-host-prohibited by default) returns errno 113 instantly. Open the port in the server's firewall.

How do I catch Errno 113 in Python?

except OSError as e: if e.errno == errno.EHOSTUNREACH. It is not a ConnectionError subclass, so except ConnectionError misses it.

Written with AI assistance. Measured on 2026-10-23 in Docker: python:3-slim (Python 3.15.1, glibc, requests from pip) and python:3-alpine (musl). The firewall test ran iptables inside its own container. Each block was run verbatim and the output pasted unedited. PlainHeaders is our extension; Python is free.