tiangolo/fastapi · error · AssertionError

A frontend path cannot be empty

Error message

A frontend path cannot be empty

What it means

Raised by `_normalize_frontend_path` (fastapi/routing.py:1855) when the `path` argument passed to `router.frontend(path, ...)` (or an internal join) is an empty string. FastAPI needs a non-empty path segment to register the frontend static-file routes, so an empty value is treated as a programmer error and fails fast with an AssertionError at app/route setup time.

Solutions

  1. Pass a path that begins with '/', typically '/': `router.frontend('/', directory='dist')`.
  2. If the path comes from configuration, default it: `frontend(os.environ.get('FRONTEND_BASE') or '/', directory='dist')`.
  3. Add an assertion or validation of the config value at startup before calling `frontend`.

Example fix

// before
router.frontend('', directory='dist')
// after
router.frontend('/', directory='dist')
Defensive patterns

Strategy: validation

Validate before calling

from fastapi import APIRouter

def safe_frontend(router: APIRouter, path: str | None, directory: str) -> None:
    if not path:
        raise ValueError('frontend path must not be empty')
    router.frontend(path, directory=directory)

Type guard

def is_valid_frontend_path(path: object) -> bool:
    return isinstance(path, str) and path != ''

Prevention

When it happens

Trigger: Calling `router.frontend('', directory='dist')` or `app.frontend('', directory='dist')`. Also triggered if a router with an empty `prefix` calls `frontend` such that `_join_frontend_paths` yields an empty combined path.

Common situations: Building the path from a variable that resolved to an empty string (e.g. `os.environ.get('FRONTEND_BASE')` returning ''). Passing `path.strip()` after trimming whitespace off a misconfigured value. Copy-pasting a `frontend(...)` call and forgetting to fill in the first positional argument.

Related errors


AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11). Data as JSON: /api/errors/89e90dcc5e28695b. Report an issue: GitHub.

Appendix: source

Thrown at fastapi/routing.py:1855

                yield RouteContext(original_route)
            else:
                yield RouteContext(original_route, route_context)


def _iter_routes_with_context(
    routes: Sequence[BaseRoute],
) -> Iterator[tuple[BaseRoute, _EffectiveRouteContext | None]]:
    for route in routes:
        if isinstance(route, _IncludedRouter):
            for route_context in route.effective_route_contexts():
                yield route_context.original_route, route_context
        else:
            yield route, None


def _normalize_frontend_path(path: str) -> str:
    if not path:
        raise AssertionError("A frontend path cannot be empty")
    if not path.startswith("/"):
        raise AssertionError("A frontend path must start with '/'")
    if path != "/":
        path = path.rstrip("/")
    return path


def _join_frontend_paths(prefix: str, path: str) -> str:
    if not prefix:
        return path
    if path == "/":
        return prefix
    return prefix + path


def _frontend_path_specificity(path: str) -> int:
    if path == "/":
        return 0

View on GitHub (pinned to 3e8d1526d8)