aio-libs/aiohttp · error · TypeError

Prefix must be str

Error message

Prefix must be str

What it means

Raised by Application.add_subapp when prefix is not a str instance. Subapps are mounted under a URL path prefix, and aiohttp both string-slices (rstrip('/')) and uses the prefix in URL matching, so a non-str type (Path, bytes, None) is rejected up front. Use add_subapp(str(prefix), subapp) if you have a pathlib.Path.

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 c0ef574e29)

Solutions

  1. Convert to str: `app.add_subapp(str(prefix), subapp)`.
  2. Keep prefixes as plain strings in config rather than Path objects.
  3. Validate the type at config load time.

Example fix

// before
app.add_subapp(pathlib.Path('/admin'), admin_app)  # Path, not str
// after
app.add_subapp('/admin', admin_app)
# or
app.add_subapp(str(pathlib.Path('/admin')), admin_app)
Defensive patterns

Strategy: type-guard

Validate before calling

def safe_add_subapp(app, prefix, subapp):
    if not isinstance(prefix, str):
        raise TypeError(f'prefix must be str, got {type(prefix).__name__}')
    return app.add_subapp(prefix, subapp)

Type guard

from typing import Any, TypeGuard

def is_str_prefix(p: Any) -> TypeGuard[str]:
    return isinstance(p, str)

Prevention

When it happens

Trigger: Calling app.add_subapp(pathlib.Path('/admin'), subapp); passing bytes prefix b'/admin'; passing None or a custom path object.

Common situations: Building prefixes from pathlib.Path; reading prefix from config that yields a non-str type; refactoring that drops a str() conversion; templating that emits None on missing key.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/e48e64d33a003ae4.json. Report an issue: GitHub.