tiangolo/fastapi · error · AssertionError
fallback must be 'auto', 'index.html', '404.html', or None
Error message
fallback must be 'auto', 'index.html', '404.html', or None
What it means
`_FrontendRoute.__init__` (routing.py:2056-2059) raises `AssertionError` when `fallback` is not one of `{"auto", "index.html", "404.html", None}`. The parameter is typed `Literal[...]`, so a static type checker flags it, but at runtime any other value (e.g. `"error.html"`, `True`, `"Auto"`) triggers this assertion at route construction.
Source
Thrown at fastapi/routing.py:2057
for media_type, quality in _iter_accept_media_types(
request.headers.get("accept", "")
):
if media_type in {"text/html", "application/xhtml+xml"} and quality != 0:
return True
return False
class _FrontendRoute(BaseRoute):
def __init__(
self,
path: str,
*,
directory: str | os.PathLike[str],
fallback: Literal["auto", "index.html", "404.html"] | None = "auto",
check_dir: bool,
) -> None:
if fallback not in {"auto", "index.html", "404.html", None}:
raise AssertionError(
"fallback must be 'auto', 'index.html', '404.html', or None"
)
self.path = _normalize_frontend_path(path)
self.methods = {"GET", "HEAD"}
self.app = _FrontendStaticFiles(
directory=directory, fallback=fallback, check_dir=check_dir
)
def matches(self, scope: Scope) -> tuple[Match, Scope]:
return self.matches_with_path(scope, self.path)
def matches_with_path(self, scope: Scope, path: str) -> tuple[Match, Scope]:
if scope["type"] != "http":
return Match.NONE, {}
frontend_path = self._get_frontend_path(path, get_route_path(scope))
if frontend_path is None:
return Match.NONE, {}
child_scope = {View on GitHub (pinned to 42a41db11f)
Solutions
- Use one of the allowed literals: `"auto"`, `"index.html"`, `"404.html"`, or `None`.
- If you need a custom page, name the file `404.html` (or `index.html`) in the directory and select that literal.
- Validate config-driven `fallback` against the allowed set before passing it to `.frontend()`.
- Run a type checker (mypy/pyright) — the `Literal` annotation will flag invalid values.
Example fix
# before
app.frontend("/", directory="dist", fallback="error.html")
# after
# rename your file to dist/404.html, then:
app.frontend("/", directory="dist", fallback="404.html") Defensive patterns
Strategy: type-guard
Validate before calling
from typing import Literal
_ALLOWED = {"auto", "index.html", "404.html", None}
def coerce_fallback(value: str | None) -> Literal["auto", "index.html", "404.html"] | None:
if value not in _ALLOWED:
raise ValueError(
f"fallback must be one of {sorted(v for v in _ALLOWED if v)}, got {value!r}"
)
return value # type: ignore[return-value]
# usage
app.frontend("/", directory="dist", fallback=coerce_fallback(cfg.get("FALLBACK", "auto"))) Type guard
from typing import Literal
def is_allowed_fallback(value: object) -> bool:
return value in {"auto", "index.html", "404.html", None}
# use as a type guard:
def to_fallback(value: object) -> Literal["auto", "index.html", "404.html"] | None:
if not is_allowed_fallback(value):
raise TypeError(f"unsupported fallback: {value!r}")
return value # type: ignore[return-value] Prevention
- Run mypy/pyright — the `Literal` annotation flags invalid values at type-check time.
- Whitelist config-driven fallback values against the allowed set.
- Name custom error pages `404.html` so they fit the supported literals.
When it happens
Trigger: Passing `app.frontend("/", directory="dist", fallback="error.html")`, `fallback="/index.html"`, `fallback=True`, or any casing variant like `"AUTO"`. Reading the value from config without validating against the allowed set.
Common situations: Wanting a custom error page name and guessing the API accepts arbitrary filenames; typos; config files supplying an unsupported string; copy-pasting an older/newer API's value.
Related errors
- A frontend path cannot be empty
- A frontend path must start with '/'
- Frontend directory '{directory}' does not exist. Resolved ab
- Frontend fallback file '{fallback}' does not exist in direct
- {"errors": exc.errors(), "body": body.decode()}
AI-assisted analysis of tiangolo/fastapi@42a41db11f (2026-08-04).
Data as JSON: /data/errors/e3a526c6cc3a4ed1.json.
Report an issue: GitHub.