{"id":"4a809e80a070c5d5","repo":"aio-libs/aiohttp","slug":"either-both-host-and-port-or-none-of-them-are-allo","errorCode":null,"errorMessage":"either both host and port or none of them are allowed","messagePattern":"either both host and port or none of them are allowed","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/connector.py","lineNumber":1118,"sourceCode":"\n        return waiters\n\n    @property\n    def family(self) -> int:\n        \"\"\"Socket family like AF_INET.\"\"\"\n        return self._family\n\n    @property\n    def use_dns_cache(self) -> bool:\n        \"\"\"True if local DNS caching is enabled.\"\"\"\n        return self._use_dns_cache\n\n    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,","sourceCodeStart":1100,"sourceCodeEnd":1136,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/connector.py#L1100-L1136","documentation":"Raised by TCPConnector.clear_dns_cache() when the caller passes host without port (or port without host). The cache is keyed by (host, port) tuples, so partial keys are ambiguous; aiohttp refuses rather than guess. Pass both to evict one entry, or neither to flush the whole cache.","triggerScenarios":"Calling `connector.clear_dns_cache(host='example.com')` or `connector.clear_dns_cache(port=443)` with the other argument omitted.","commonSituations":"Helper code that conditionally fills host/port and forgets the other. Tests that try to invalidate a single host but pass only the hostname. Refactor that dropped one argument by accident.","solutions":["Pass both: `connector.clear_dns_cache('example.com', 443)`.","Or flush everything: `connector.clear_dns_cache()` with no args."],"exampleFix":"# before\nconnector.clear_dns_cache(host='example.com')\n# after\nconnector.clear_dns_cache('example.com', 443)","handlingStrategy":"validation","validationCode":"def clear_cache(connector, host=None, port=None):\n    if (host is None) != (port is None):\n        raise ValueError('pass both host and port, or neither')\n    if host is None:\n        connector.clear_dns_cache()\n    else:\n        connector.clear_dns_cache(host, port)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Wrap clear_dns_cache in a helper that enforces the both-or-neither rule.","Document the cache keying in your connector wrapper so callers pass tuples.","Add a quick unit test for your helper covering the three valid call shapes."],"tags":["connector","dns","validation","cache"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}