aio-libs/aiohttp · error · ValueError

Prefix cannot be empty

Error message

Prefix cannot be empty

What it means

Raised by Application.add_subapp when prefix, after rstrip('/'), is empty. A subapp must be scoped under a non-empty path segment; mounting at '' would intercept every request including ones meant for the parent, which is ambiguous and almost never intended. aiohttp rejects this with ValueError rather than guessing the user meant the root.

Source

Thrown at aiohttp/web_app.py:280

        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()
        return resource

    def add_domain(self, domain: str, subapp: "Application") -> MatchedSubAppResource:

View on GitHub (pinned to c0ef574e29)

Solutions

  1. Provide a non-empty, non-/ prefix: '/v1', '/admin', etc.
  2. If you actually want everything routed through one app, do not use add_subapp — configure that app directly.
  3. Validate at config load: `if not prefix or prefix == '/': raise ValueError('non-root prefix required')`.

Example fix

// before
app.add_subapp('/', subapp)  # ValueError
// after
app.add_subapp('/api', subapp)
Defensive patterns

Strategy: validation

Validate before calling

def safe_add_subapp(app, prefix, subapp):
    if not isinstance(prefix, str):
        raise TypeError('prefix must be str')
    prefix = prefix.rstrip('/')
    if not prefix:
        raise ValueError('prefix cannot be empty')
    return app.add_subapp(prefix, subapp)

Prevention

When it happens

Trigger: Calling app.add_subapp('', subapp); add_subapp('/', subapp) (becomes '' after rstrip); add_subapp('///', subapp) (also collapses to ''); dynamically computed prefix that resolves to root.

Common situations: Default value of an optional config prefix; templating that yields '/' for the root deployment; refactoring that forgot to set the prefix; attempt to mount a default subapp at root.

Related errors


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