{"id":"89e90dcc5e28695b","repo":"tiangolo/fastapi","slug":"a-frontend-path-cannot-be-empty","errorCode":null,"errorMessage":"A frontend path cannot be empty","messagePattern":"A frontend path cannot be empty","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"fastapi/routing.py","lineNumber":1855,"sourceCode":"                yield RouteContext(original_route)\n            else:\n                yield RouteContext(original_route, route_context)\n\n\ndef _iter_routes_with_context(\n    routes: Sequence[BaseRoute],\n) -> Iterator[tuple[BaseRoute, _EffectiveRouteContext | None]]:\n    for route in routes:\n        if isinstance(route, _IncludedRouter):\n            for route_context in route.effective_route_contexts():\n                yield route_context.original_route, route_context\n        else:\n            yield route, None\n\n\ndef _normalize_frontend_path(path: str) -> str:\n    if not path:\n        raise AssertionError(\"A frontend path cannot be empty\")\n    if not path.startswith(\"/\"):\n        raise AssertionError(\"A frontend path must start with '/'\")\n    if path != \"/\":\n        path = path.rstrip(\"/\")\n    return path\n\n\ndef _join_frontend_paths(prefix: str, path: str) -> str:\n    if not prefix:\n        return path\n    if path == \"/\":\n        return prefix\n    return prefix + path\n\n\ndef _frontend_path_specificity(path: str) -> int:\n    if path == \"/\":\n        return 0","sourceCodeStart":1837,"sourceCodeEnd":1873,"githubUrl":"https://github.com/tiangolo/fastapi/blob/42a41db11f6882807ac3c057b942178d53b97438/fastapi/routing.py#L1837-L1873","documentation":"`_normalize_frontend_path` (routing.py:1853-1860) rejects an empty path with `AssertionError`. It is called by `APIRouter.frontend()` (routing.py:2704) when you register a static frontend mount. The path is the URL prefix under which the built frontend is served, and an empty prefix is ambiguous, so FastAPI aborts at router/app construction time.","triggerScenarios":"Calling `app.frontend(\"\", directory=\"dist\")` or `router.frontend(\"\", directory=...)`. Passing a computed path variable that resolves to `\"\"`.","commonSituations":"Building the path from config/env that is unset and defaults to empty; copy-pasting an example but forgetting the leading segment; dynamically composing `f\"{base}\"` where base is empty.","solutions":["Pass a non-empty path beginning with `/`, typically `\"/\"` for a root-mounted SPA.","If the mount point is configurable, default it to `\"/\"` and validate before calling `.frontend(...)`.","Check for empty/None before invoking `.frontend()` and raise a clear config error of your own."],"exampleFix":"# before\napp.frontend(\"\", directory=\"dist\")\n\n# after\napp.frontend(\"/\", directory=\"dist\")","handlingStrategy":"validation","validationCode":"def assert_frontend_path(path: str) -> str:\n    if not isinstance(path, str) or path == \"\":\n        raise ValueError(\"frontend path must be a non-empty string starting with '/'\")\n    if not path.startswith(\"/\"):\n        path = \"/\" + path.lstrip(\"/\")\n    return path\n\n# usage\nfp = assert_frontend_path(config.get(\"FRONTEND_PATH\", \"/\"))\napp.frontend(fp, directory=\"dist\")","typeGuard":"def is_valid_frontend_path(path: object) -> bool:\n    return isinstance(path, str) and len(path) > 0 and path.startswith(\"/\")","tryCatchPattern":null,"preventionTips":["Default configurable frontend paths to `/` rather than empty string.","Validate config values once at startup before building the app.","Keep `.frontend()` calls behind a small helper that normalizes the path."],"tags":["frontend","routing","configuration","static-files"],"analyzedSha":"42a41db11f6882807ac3c057b942178d53b97438","analyzedAt":"2026-08-04T19:23:32.007Z","schemaVersion":2}