aio-libs/aiohttp · error · TypeError
Domain must be str
Error message
Domain must be str
What it means
Raised by Application.add_domain when domain is not a str instance. add_domain mounts a subapp against a Host-header rule, and the rule is matched/compiled as a string (MaskDomain uses shell-style globbing on the host). A non-str domain (bytes, None, ipaddress object) cannot be matched against the Host header, so aiohttp rejects it up front with TypeError.
Source
Thrown at aiohttp/web_app.py:300
return self._add_subapp(factory, subapp)
def _add_subapp(
self, resource_factory: Callable[[], _Resource], subapp: "Application"
) -> _Resource:
if self.frozen:
raise RuntimeError("Cannot add sub application to frozen application")
if subapp.frozen:
raise RuntimeError("Cannot add frozen application")
resource = resource_factory()
self.router.register_resource(resource)
self._reg_subapp_signals(subapp)
self._subapps.append(subapp)
subapp.pre_freeze()
return resource
def add_domain(self, domain: str, subapp: "Application") -> MatchedSubAppResource:
if not isinstance(domain, str):
raise TypeError("Domain must be str")
elif "*" in domain:
rule: Domain = MaskDomain(domain)
else:
rule = Domain(domain)
factory = partial(MatchedSubAppResource, rule, subapp)
return self._add_subapp(factory, subapp)
def add_routes(self, routes: Iterable[AbstractRouteDef]) -> list[AbstractRoute]:
return self.router.add_routes(routes)
@property
def on_response_prepare(self) -> _RespPrepareSignal:
return self._on_response_prepare
@property
def on_startup(self) -> _AppSignal:
return self._on_startup
View on GitHub (pinned to c0ef574e29)
Solutions
- Pass a str domain: app.add_domain('api.example.com', subapp).
- If you have a URL object, use parsed.url or parsed.host.
- Coerce bytes via .decode('ascii'); reject None explicitly.
- Validate at config load time.
Example fix
// before
app.add_domain(b'api.example.com', subapp) # bytes
app.add_domain(parsed, subapp) # urlparse result
// after
app.add_domain('api.example.com', subapp)
app.add_domain(parsed.host, subapp) Defensive patterns
Strategy: type-guard
Validate before calling
def safe_add_domain(app, domain, subapp):
if not isinstance(domain, str):
raise TypeError(f'domain must be str, got {type(domain).__name__}')
return app.add_domain(domain, subapp) Type guard
from typing import Any, TypeGuard
def is_str_domain(d: Any) -> TypeGuard[str]:
return isinstance(d, str) Prevention
- Pass host as a str literal or parsed.host.
- Decode bytes domains at the config boundary.
- Reject None domains explicitly at config load.
When it happens
Trigger: Calling app.add_domain(b'api.example.com', subapp); passing an ipaddress.IPv4Address; passing None when no domain config is present; passing a parsed URL object instead of its .host attribute.
Common situations: Reading the domain from config that yields bytes; building from urllib.parse.urlparse and forgetting .host; templating that emits None on missing key; mixing add_domain (Host-based) with add_subapp (path-based) and confusing the argument type.
Related errors
- Prefix must be str
- Prefix cannot be empty
- Cannot add sub application to frozen application
- Cannot add frozen application
- Compress wbits must between 9 and 15, zlib does not support
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/bd1d5e4d0e8de741.json.
Report an issue: GitHub.