{"id":"fec920ba5e999b6f","repo":"aio-libs/aiohttp","slug":"host-is-not-a-canonical-ipv4-address","errorCode":null,"errorMessage":"{host} - is not a canonical IPv4 address","messagePattern":"(.+?) - is not a canonical IPv4 address","errorType":"exception","errorClass":"InvalidUrlClientError","httpStatus":null,"severity":"error","filePath":"aiohttp/connector.py","lineNumber":1131,"sourceCode":"    def clear_dns_cache(self, host: str | None = None, port: int | None = None) -> None:\n        \"\"\"Remove specified host/port or clear all dns local cache.\"\"\"\n        if host is not None and port is not None:\n            self._cached_hosts.remove((host, port))\n        elif host is not None or port is not None:\n            raise ValueError(\"either both host and port or none of them are allowed\")\n        else:\n            self._cached_hosts.clear()\n\n    async def _resolve_host(\n        self, host: str, port: int, traces: Sequence[\"Trace\"] | None = None\n    ) -> list[ResolveResult]:\n        \"\"\"Resolve host and return list of addresses.\"\"\"\n        if is_ip_address(host):\n            # Reject legacy numeric IPv4 forms (e.g. 2130706433, 127.1) that\n            # socket would map onto an address, slipping past a connector-level\n            # policy that only sees the raw host.\n            if \":\" not in host and not is_canonical_ipv4_address(host):\n                raise InvalidUrlClientError(host, \"is not a canonical IPv4 address\")\n            return [\n                {\n                    \"hostname\": host,\n                    \"host\": host,\n                    \"port\": port,\n                    \"family\": self._family,\n                    \"proto\": 0,\n                    \"flags\": 0,\n                }\n            ]\n\n        if not self._use_dns_cache:\n            if traces:\n                for trace in traces:\n                    await trace.send_dns_resolvehost_start(host)\n\n            if self._closed:\n                raise ClientConnectionError(\"Connector is closed\")","sourceCodeStart":1113,"sourceCodeEnd":1149,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/connector.py#L1113-L1149","documentation":"Raised by TCPConnector._resolve_host() when the URL host parses as an IP literal but is a legacy non-canonical IPv4 form (e.g. `2130706433`, `127.1`, `0x7f000001`). aiohttp now requires dotted-quad canonical form (`127.0.0.1`) because the legacy forms can slip past connector-level host policies. The check is `is_canonical_ipv4_address` in helpers.py.","triggerScenarios":"Requesting a URL whose host is `http://2130706433/` or `http://127.1/` - forms that socket would accept but aiohttp rejects, specifically when the host has no colon (IPv4 path) and fails the canonical check.","commonSituations":"User-supplied or scraped URLs using compact IPv4 notation. Obfuscated URLs / security research payloads. Configs that derive host from integer identifiers.","solutions":["Normalize the host to dotted-quad with `ipaddress.ip_address(host).exploded` / socket.inet_ntoa before building the URL.","Reject or rewrite non-canonical IPv4 inputs at your URL validation boundary.","If the integer is intentional, convert it explicitly: `socket.inet_ntoa(struct.pack('!I', int(host)))`."],"exampleFix":"# before\nurl = 'http://2130706433/'\n# after\nimport ipaddress\nhost = str(ipaddress.ip_address(2130706433))  # '127.0.0.1'\nurl = f'http://{host}/'","handlingStrategy":"validation","validationCode":"import ipaddress\n\ndef canonicalize_host(host: str) -> str:\n    try:\n        return str(ipaddress.ip_address(host))\n    except ValueError:\n        return host  # leave DNS names alone","typeGuard":"def is_canonical_ipv4(host: str) -> bool:\n    parts = host.split('.')\n    if len(parts) != 4:\n        return False\n    return all(p.isdigit() and 0 <= int(p) <= 255 and (p == '0' or not p.startswith('0')) for p in parts)","tryCatchPattern":"from aiohttp import InvalidUrlClientError\ntry:\n    await session.get(url)\nexcept InvalidUrlClientError as e:\n    if 'canonical IPv4' in str(e):\n        # normalize and retry\n        ...\n    raise","preventionTips":["Validate user-supplied URLs at the trust boundary with urllib/yparser before passing to aiohttp.","Normalize integer/compact IPv4 forms to dotted-quad explicitly when they are intentional.","Log rejected hosts so obfuscated-input attempts are visible."],"tags":["url","ipv4","validation","security","connector"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}