{"id":"f0b3e9492e5ca24d","repo":"encode/httpx","slug":"invalid-idna-hostname-host-r","errorCode":null,"errorMessage":"Invalid IDNA hostname: {host!r}","messagePattern":"Invalid IDNA hostname: (.+?)","errorType":"validation","errorClass":"InvalidURL","httpStatus":null,"severity":"error","filePath":"httpx/_urlparse.py","lineNumber":392,"sourceCode":"            ipaddress.IPv6Address(host[1:-1])\n        except ipaddress.AddressValueError:\n            raise InvalidURL(f\"Invalid IPv6 address: {host!r}\")\n        return host[1:-1]\n\n    elif host.isascii():\n        # Regular ASCII hostnames\n        #\n        # From https://datatracker.ietf.org/doc/html/rfc3986/#section-3.2.2\n        #\n        # reg-name    = *( unreserved / pct-encoded / sub-delims )\n        WHATWG_SAFE = '\"`{}%|\\\\'\n        return quote(host.lower(), safe=SUB_DELIMS + WHATWG_SAFE)\n\n    # IDNA hostnames\n    try:\n        return idna.encode(host.lower()).decode(\"ascii\")\n    except idna.IDNAError:\n        raise InvalidURL(f\"Invalid IDNA hostname: {host!r}\")\n\n\ndef normalize_port(port: str | int | None, scheme: str) -> int | None:\n    # From https://tools.ietf.org/html/rfc3986#section-3.2.3\n    #\n    # \"A scheme may define a default port.  For example, the \"http\" scheme\n    # defines a default port of \"80\", corresponding to its reserved TCP\n    # port number.  The type of port designated by the port number (e.g.,\n    # TCP, UDP, SCTP) is defined by the URI scheme.  URI producers and\n    # normalizers should omit the port component and its \":\" delimiter if\n    # port is empty or if its value would be the same as that of the\n    # scheme's default.\"\n    if port is None or port == \"\":\n        return None\n\n    try:\n        port_as_int = int(port)\n    except ValueError:","sourceCodeStart":374,"sourceCodeEnd":410,"githubUrl":"https://github.com/encode/httpx/blob/b5addb64f0161ff6bfe94c124ef76f6a1fba5254/httpx/_urlparse.py#L374-L410","documentation":"Raised by encode_host for non-ASCII hostnames when idna.encode() raises IDNAError. httpx uses the idna library to convert internationalized domain names to punycode; inputs that violate IDNA 2008 (e.g. illegal characters, overly long labels > 63 chars, empty labels) cannot be encoded and are rejected.","triggerScenarios":"httpx.URL('http://exa mple.com/'), httpx.URL(host='xn--invalid'), a host with a label longer than 63 characters, or a host containing a character idna rejects (e.g. underscore in some positions).","commonSituations":"User-typed URLs with spaces or underscores; copy-paste introducing zero-width characters; very long single-label hostnames; mixing valid IDNA with disallowed punctuation.","solutions":["Sanitize the hostname: strip whitespace and disallowed chars before encoding.","Validate each dot-separated label is 1-63 chars and matches RFC 1035 (letters/digits/hyphen).","Use httpx.URL(...) with a pre-encoded punycode host (xn--...) if you manage IDNA yourself.","Fall back to a known-good hostname when user input is unparseable."],"exampleFix":"// before\nurl = httpx.URL(\"http://exa mple.com/\")  # Invalid IDNA hostname\n\n// after\nhost = \"exa mple.com\".replace(\" \", \"\")\nurl = httpx.URL(f\"http://{host}/\")","handlingStrategy":"validation","validationCode":"import re\nimport idna\n\ndef safe_host(host: str) -> str:\n    host = host.strip().lower()\n    for label in host.split(\".\"):\n        if not label or len(label) > 63:\n            raise ValueError(f\"Bad host label in {host!r}\")\n    try:\n        idna.encode(host)\n    except idna.IDNAError as e:\n        raise ValueError(f\"Invalid IDNA hostname {host!r}\") from e\n    return host\n\nurl = httpx.URL(host=safe_host(user_host))","typeGuard":"import idna\n\ndef is_valid_host(host: str) -> bool:\n    try:\n        idna.encode(host)\n        return True\n    except idna.IDNAError:\n        return False","tryCatchPattern":"from httpx import InvalidURL\n\ntry:\n    url = httpx.URL(f\"http://{host}/\")\nexcept InvalidURL as e:\n    if \"Invalid IDNA\" in str(e):\n        # fall back to a punycode-encoded host or reject\n        raise ValueError(f\"Unusable hostname {host!r}\") from e\n    raise","preventionTips":["Strip whitespace and disallowed punctuation from hostnames.","Validate each label is 1-63 chars and letters/digits/hyphen.","Reject underscores in hostnames unless you control DNS."],"tags":["url","idna","hostname","validation","i18n"],"analyzedSha":"b5addb64f0161ff6bfe94c124ef76f6a1fba5254","analyzedAt":"2026-08-04T19:32:56.768Z","schemaVersion":2}