encode/httpx · error · InvalidURL

Invalid IPv4 address: {host!r}

Error message

Invalid IPv4 address: {host!r}

What it means

Raised by encode_host when the host looks like an IPv4 dotted-quad (matches ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$) but ipaddress.IPv4Address() rejects it (e.g. an octet > 255 or negative). Each octet must be a valid 0-255 decimal; leading zeros are also rejected by modern Python.

Source

Thrown at httpx/_urlparse.py:361

        parsed_query,
        parsed_frag,
    )


def encode_host(host: str) -> str:
    if not host:
        return ""

    elif IPv4_STYLE_HOSTNAME.match(host):
        # Validate IPv4 hostnames like #.#.#.#
        #
        # From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2
        #
        # IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
        try:
            ipaddress.IPv4Address(host)
        except ipaddress.AddressValueError:
            raise InvalidURL(f"Invalid IPv4 address: {host!r}")
        return host

    elif IPv6_STYLE_HOSTNAME.match(host):
        # Validate IPv6 hostnames like [...]
        #
        # From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2
        #
        # "A host identified by an Internet Protocol literal address, version 6
        # [RFC3513] or later, is distinguished by enclosing the IP literal
        # within square brackets ("[" and "]").  This is the only place where
        # square bracket characters are allowed in the URI syntax."
        try:
            ipaddress.IPv6Address(host[1:-1])
        except ipaddress.AddressValueError:
            raise InvalidURL(f"Invalid IPv6 address: {host!r}")
        return host[1:-1]

    elif host.isascii():

View on GitHub (pinned to b5addb64f0)

Solutions

  1. Validate the IP with ipaddress.IPv4Address(host) before building the URL.
  2. Clamp generated octets to 0-255 in any code that synthesizes IPs.
  3. Strip whitespace/dots from user input before parsing.
  4. If a hostname (not an IP) is intended, ensure it does not match the dotted-quad regex.

Example fix

// before
url = httpx.URL("http://10.0.0.300/")  # Invalid IPv4 address

// after
import ipaddress
host = "10.0.0.300"
ipaddress.IPv4Address(host)  # raises early, predictable
url = httpx.URL("http://10.0.0.30/")
Defensive patterns

Strategy: validation

Validate before calling

import ipaddress

def safe_ipv4(host: str) -> str:
    ipaddress.IPv4Address(host)  # raises AddressValueError early
    return host

url = httpx.URL(host=safe_ipv4(user_host))

Type guard

import ipaddress

def is_valid_ipv4(host: str) -> bool:
    try:
        ipaddress.IPv4Address(host)
        return True
    except ipaddress.AddressValueError:
        return False

Try / catch

from httpx import InvalidURL

try:
    url = httpx.URL(f"http://{host}/")
except InvalidURL as e:
    if "Invalid IPv4" in str(e):
        # fall back to DNS hostname or reject input
        raise ValueError(f"Bad IP from user: {host!r}") from e
    raise

Prevention

When it happens

Trigger: httpx.URL('http://999.0.0.1/'), httpx.URL(host='10.0.0.256'), or constructing a URL with host='192.168.1.300'.

Common situations: Off-by-one IP generation; user-typed IP with a typo; arithmetic that produces an octet > 255; legacy code emitting leading-zero octets like '010.0.0.1'.

Related errors


AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04). Data as JSON: /data/errors/435d08526a962d73.json. Report an issue: GitHub.