tiangolo/fastapi · error · FastAPIError
Prefix and path cannot be both empty
Error message
Prefix and path cannot be both empty (path operation: {name}) What it means
Raised in `APIRouter.include_router` (fastapi/routing.py:3293) as `FastAPIError` when `prefix` is empty/falsy AND one of the included router's routes has an empty `path`. FastAPI forbids a route with both empty prefix and empty path because it would be ambiguous (it would match everything). The offending path operation's name is included so you can locate it.
Solutions
- Add a non-empty prefix on include: `app.include_router(sub, prefix='/sub')`.
- Fix the empty route path to use '/': `@sub.get('/')` instead of `@sub.get('')`.
- Ensure every path operation in the included router has a path starting with '/'.
Example fix
// before
@sub.get('')
async def root(): ...
app.include_router(sub) # prefix empty, path empty
// after
@sub.get('/')
async def root(): ...
app.include_router(sub, prefix='/sub') Defensive patterns
Strategy: validation
Validate before calling
def safe_include(parent, sub, prefix: str | None) -> None:
prefix = prefix or ''
if not prefix:
for r in getattr(sub, 'routes', []):
p = getattr(r, 'path', None)
if p is not None and not p:
raise ValueError(f'sub-router has an empty path; add a prefix or fix {getattr(r, "name", "?")}')
parent.include_router(sub, prefix=prefix) Type guard
def include_is_safe(sub, prefix: object) -> bool:
if isinstance(prefix, str) and prefix:
return True
return all(getattr(r, 'path', '/') for r in getattr(sub, 'routes', [])) Prevention
- Always give every path operation a path starting with '/'.
- When dropping an include prefix, audit sub-router paths first.
- Add a startup smoke test that builds the app to catch include-time errors early.
When it happens
Trigger: Including a sub-router with no prefix (`include_router(sub)` or `include_router(sub, prefix='')`) when `sub` contains a route declared with `@sub.get('')` or `@sub.route('')`. Also occurs when a sub-router itself was created with an empty path and is included without a prefix.
Common situations: Refactoring where a prefix was removed from `include_router` but the route paths were not updated to start with '/'. Declaring `@router.get('')` expecting it to mean '/'. Copying routers between apps and dropping the prefix argument.
Related errors
- A frontend path cannot be empty
- A frontend path must start with '/'
- fallback must be 'auto', 'index.html', '404.html', or None
- Frontend directory ' ' does not exist. Resolved absolute…
- Frontend fallback file
AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11).
Data as JSON: /api/errors/9ed5c86d5157d5e2.
Report an issue: GitHub.
Appendix: source
Thrown at fastapi/routing.py:3293
)
if prefix:
assert prefix.startswith("/"), "A path prefix must start with '/'"
assert not prefix.endswith("/"), (
"A path prefix must not end with '/', as the routes will start with '/'"
)
else:
for route, route_context in _iter_routes_with_context(router.routes):
if route_context is None:
path = getattr(route, "path", None)
name = getattr(route, "name", "unknown")
elif route_context.starlette_route is not None:
path = getattr(route_context.starlette_route, "path", None)
name = getattr(route_context.starlette_route, "name", "unknown")
else:
path = route_context.path
name = route_context.name
if path is not None and not path:
raise FastAPIError(
f"Prefix and path cannot be both empty (path operation: {name})"
)
include_context = _RouterIncludeContext.for_include(
parent_router=self,
included_router=router,
prefix=prefix,
tags=tags,
dependencies=dependencies,
default_response_class=default_response_class,
responses=responses,
callbacks=callbacks,
deprecated=deprecated,
include_in_schema=include_in_schema,
generate_unique_id_function=generate_unique_id_function,
)
self.routes.append(
_IncludedRouter(original_router=router, include_context=include_context)
)View on GitHub (pinned to 3e8d1526d8)