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

  1. Ensure prefix is a str before calling: app.add_subapp(str(prefix), subapp).
  2. Guard against None: if prefix is not None: app.add_subapp(prefix, subapp).
  3. 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

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


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)