aio-libs/aiohttp · error · TypeError
Domain must be str
Error message
Domain must be str
What it means
DomainValidator (used by DomainMatchRule / the Host matcher) requires its domain argument to be a str. The validation method checks isinstance(domain, str) and raises TypeError if not, because all subsequent processing (rstrip, lower, URL parsing, regex matching) assumes string operations.
Solutions
- Pass the domain as a str, e.g. 'example.com' or 'example.com:8080'.
- If you have a URL object, extract url.raw_host (and url.port if non-default) and pass the host string.
- Decode bytes to str before passing.
Example fix
// before
rule = Domain(b'example.com') # bytes
// after
rule = Domain('example.com') # str Defensive patterns
Strategy: type-guard
Validate before calling
def validate_domain(domain):
if not isinstance(domain, str):
raise TypeError(f'Domain must be str, got {type(domain).__name__}')
return domain Type guard
def is_domain_str(domain) -> bool:
return isinstance(domain, str) Prevention
- Always pass the host as a str, not bytes or a URL object.
- Extract url.raw_host from yarl.URL before constructing a Domain rule.
- Decode bytes to str at the boundary where low-level data enters.
When it happens
Trigger: Constructing a domain-based matcher with a non-string: e.g. passing bytes (b'example.com'), an integer (port number), None, or a yarl.URL object instead of the host string. This typically happens when building a host-based routing rule (app.router.add_resource with host constraints) or the Domain middleware.
Common situations: Passing a URL object where the host string is expected; passing bytes from a low-level parser; configuration loaded from a binary source; off-by-one extraction that grabs the wrong field.
Related errors
- Domain cannot be empty
- Domain must be str
- Only async functions are allowed as web-handlers, got
- access_log_class must be subclass of…
- Added route will never be executed, method
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/47cb43fc3ac6fe16.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/web_urldispatcher.py:781
@abc.abstractmethod # pragma: no branch
def canonical(self) -> str:
"""Return a str"""
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)View on GitHub (pinned to d041d4d0fd)