aio-libs/aiohttp · error · TypeError
Prefix must be str
Error message
Prefix must be str
What it means
Raised by Application.add_subapp (and the same check is mirrored in add_domain) when the prefix argument is not a str. add_subapp requires a URL path prefix as a string because it builds a PrefixedSubAppResource that matches incoming paths against it. Passing None, bytes, or any non-string type is rejected immediately.
Solutions
- Ensure prefix is a str before calling: app.add_subapp(str(prefix), subapp).
- Guard against None: if prefix is not None: app.add_subapp(prefix, subapp).
- Validate config values at load time so prefix is always a non-empty string.
Example fix
# before
app.add_subapp(config_prefix, subapp) # config_prefix may be None
# after
if isinstance(config_prefix, str):
app.add_subapp(config_prefix, subapp)
else:
raise TypeError('subapp prefix must be a str from config') Defensive patterns
Strategy: type-guard
Validate before calling
if not isinstance(prefix, str):
raise TypeError('prefix must be str')
app.add_subapp(prefix, subapp) Type guard
def valid_prefix(p) -> bool:
return isinstance(p, str) and p.strip('/') != '' Prevention
- Coerce prefix to str and validate non-emptiness before add_subapp.
- Reject None/Optional prefixes from config at load time.
- Unit-test subapp mounting with config-derived prefixes.
When it happens
Trigger: Calling app.add_subapp(None, subapp); passing a prefix loaded from config that was not converted to str; passing a pathlib.Path or bytes object as the prefix.
Common situations: Conditionally adding a subapp where prefix may be unset (None); config-driven prefix loaded as bytes; refactoring that introduced an Optional[str] without handling None.
Related errors
- Domain must be str
- Prefix cannot be empty
- Cannot add frozen application
- Cannot add sub application to frozen application
- Added route will never be executed, method
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/e48e64d33a003ae4.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/web_app.py:277
return asyncio.get_running_loop().get_debug()
def _reg_subapp_signals(self, subapp: "Application") -> None:
def reg_handler(signame: str) -> None:
subsig = getattr(subapp, signame)
async def handler(app: "Application") -> None:
await subsig.send(subapp)
appsig = getattr(self, signame)
appsig.append(handler)
reg_handler("on_startup")
reg_handler("on_shutdown")
reg_handler("on_cleanup")
def add_subapp(self, prefix: str, subapp: "Application") -> PrefixedSubAppResource:
if not isinstance(prefix, str):
raise TypeError("Prefix must be str")
prefix = prefix.rstrip("/")
if not prefix:
raise ValueError("Prefix cannot be empty")
factory = partial(PrefixedSubAppResource, prefix, subapp)
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()View on GitHub (pinned to d041d4d0fd)