aio-libs/aiohttp · error · ValueError
Domain cannot be empty
Error message
Domain cannot be empty
What it means
After confirming the domain is a str, DomainValidator strips trailing dots and lowercases; if the result is empty (e.g. the input was '' or only dots like '...'), it raises ValueError. An empty domain cannot match any Host header, so the rule would be a no-op — aiohttp rejects it at construction to surface the misconfiguration immediately.
Solutions
- Provide a non-empty domain string after stripping dots.
- Validate config at startup — fail loudly if the domain env var is empty rather than passing it through to Domain().
- Default to a concrete hostname (e.g. 'localhost' for dev) when the config value is missing.
Example fix
// before
import os
rule = Domain(os.environ.get('DOMAIN', '')) # '' if unset → ValueError
// after
import os
domain = os.environ.get('DOMAIN')
if not domain:
raise SystemExit('DOMAIN env var must be set')
rule = Domain(domain) Defensive patterns
Strategy: validation
Validate before calling
def non_empty_domain(domain: str) -> str:
cleaned = domain.rstrip('.').lower()
if not cleaned:
raise ValueError('Domain is empty after stripping dots')
return cleaned Prevention
- Validate config-derived domain strings at startup before passing to Domain().
- Default to a concrete hostname (e.g. 'localhost') when the env var is unset.
- Fail fast with a clear message rather than letting an empty string reach the router.
When it happens
Trigger: Constructing a Domain rule with Domain(''), Domain('.'), Domain('...'), or a string that becomes empty after stripping trailing dots. Typically arises from misconfigured host-based routing where the domain comes from an empty config value.
Common situations: Environment variable for the domain is unset (defaults to ''); config file with a blank domain field; dynamic construction that yields an empty string when a lookup fails; trailing-dot-only strings from malformed input.
Related errors
- Bad pattern
- base_url must have a trailing '/'
- compress must be one of True, False, 'deflate', or 'gzip'
- Domain must be str
- Domain must be str
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/465f3b65fdedbbc8.
Report an issue: GitHub.
Appendix: 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._domainView on GitHub (pinned to d041d4d0fd)