aio-libs/aiohttp · error · ValueError

Domain cannot be empty

Error message

Domain cannot be empty

What it means

Raised as ValueError by Domain.validation when, after stripping trailing dots and lowercasing, the domain string is empty. A host rule with no hostname cannot match anything meaningful, so it is rejected. Triggered via Domain(''), Domain('.'), or Domain(' ') after normalization.

Source

Thrown at aiohttp/web_urldispatcher.py:784


class Domain(AbstractRuleMatching):
    re_part = re.compile(r"(?!-)[a-z\d-]{1,63}(?<!-)")

    def __init__(self, domain: str) -> None:
        super().__init__()
        self._domain = self.validation(domain)

    @property
    def canonical(self) -> str:
        return self._domain

    def validation(self, domain: str) -> str:
        if not isinstance(domain, str):
            raise TypeError("Domain must be str")
        domain = domain.rstrip(".").lower()
        if not domain:
            raise ValueError("Domain cannot be empty")
        elif "://" in domain:
            raise ValueError("Scheme not supported")
        url = URL("http://" + domain)
        assert url.raw_host is not None
        if not all(self.re_part.fullmatch(x) for x in url.raw_host.split(".")):
            raise ValueError("Domain not valid")
        if url.port == 80:
            return url.raw_host
        return f"{url.raw_host}:{url.port}"

    async def match(self, request: Request) -> bool:
        host = request.headers.get(hdrs.HOST)
        if not host:
            return False
        return self.match_domain(host)

    def match_domain(self, host: str) -> bool:
        return host.lower() == self._domain

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Provide a real hostname: Domain('example.com').
  2. Filter empty values from config lists before constructing Domain rules.
  3. Guard config with: if host: app.add_domain(host, sub_app).

Example fix

# before
host = os.environ.get('HOST', '')
app.add_domain(host, sub_app)  # '' raises

# after
host = os.environ.get('HOST', '')
if host:
    app.add_domain(host, sub_app)
Defensive patterns

Strategy: validation

Validate before calling

def clean_domain(domain: str) -> str:
    d = domain.strip().rstrip('.').lower()
    if not d:
        raise ValueError('Domain cannot be empty')
    return d

Type guard

def is_nonempty_domain(domain) -> bool:
    return isinstance(domain, str) and bool(domain.strip().rstrip('.'))

Try / catch

try:
    app.add_domain(host, sub_app)
except ValueError as e:
    if 'empty' in str(e):
        log.warning('skipping empty host rule')
    else:
        raise

Prevention

When it happens

Trigger: Constructing aiohttp.web.Domain('') or Domain('.'), or app.add_domain('', sub_app). Also when a host variable from config is an empty string or only whitespace/ dots.

Common situations: Empty host config value; environment variable unset (defaulting to ''); building domain rules from a list that contained an empty element.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/465f3b65fdedbbc8.json. Report an issue: GitHub.