aio-libs/aiohttp · error · ValueError

either both host and port or none of them are allowed

Error message

either both host and port or none of them are allowed

What it means

Raised by clear_dns_cache() when exactly one of host or port is supplied. The cache is keyed by (host, port) tuples, so flushing a single host without its port (or vice versa) is ambiguous. aiohttp forces the caller to be explicit: both, to evict one entry, or neither, to wipe the cache.

Solutions

  1. Pass both host and port to evict a specific entry.
  2. Pass neither (call clear_dns_cache()) to wipe the entire cache.
  3. Carry host and port as a pair through your config so they are always flushed together.
  4. Validate that both are non-None before calling if your inputs are optional.

Example fix

# before
connector.clear_dns_cache(host='example.com')
# after
connector.clear_dns_cache(host='example.com', port=443)
# or wipe everything
connector.clear_dns_cache()
Defensive patterns

Strategy: validation

Validate before calling

def clear_cache(connector, host=None, port=None):
    if (host is None) != (port is None):
        raise ValueError('pass both host and port, or neither')
    if host is None:
        connector.clear_dns_cache()
    else:
        connector.clear_dns_cache(host, port)

Type guard

null

Try / catch

try:
    connector.clear_dns_cache(host=host, port=port)
except ValueError as exc:
    if 'either both' in str(exc):
        connector.clear_dns_cache()  # wipe entire cache as a safe fallback
    else:
        raise

Prevention

When it happens

Trigger: Calling connector.clear_dns_cache(host='example.com') without a port; calling clear_dns_cache(port=443) alone; looping over hosts and forgetting to pair them with ports.

Common situations: Building a tool that clears DNS for a service discovery refresh but only knows the hostname; refactoring that split host and port into separate config keys.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/4a809e80a070c5d5. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/connector.py:1118

        return waiters

    @property
    def family(self) -> int:
        """Socket family like AF_INET."""
        return self._family

    @property
    def use_dns_cache(self) -> bool:
        """True if local DNS caching is enabled."""
        return self._use_dns_cache

    def clear_dns_cache(self, host: str | None = None, port: int | None = None) -> None:
        """Remove specified host/port or clear all dns local cache."""
        if host is not None and port is not None:
            self._cached_hosts.remove((host, port))
        elif host is not None or port is not None:
            raise ValueError("either both host and port or none of them are allowed")
        else:
            self._cached_hosts.clear()

    async def _resolve_host(
        self, host: str, port: int, traces: Sequence["Trace"] | None = None
    ) -> list[ResolveResult]:
        """Resolve host and return list of addresses."""
        if is_ip_address(host):
            # Reject legacy numeric IPv4 forms (e.g. 2130706433, 127.1) that
            # socket would map onto an address, slipping past a connector-level
            # policy that only sees the raw host.
            if ":" not in host and not is_canonical_ipv4_address(host):
                raise InvalidUrlClientError(host, "is not a canonical IPv4 address")
            return [
                {
                    "hostname": host,
                    "host": host,
                    "port": port,

View on GitHub (pinned to d041d4d0fd)