{"id":"e3a526c6cc3a4ed1","repo":"tiangolo/fastapi","slug":"fallback-must-be-auto-index-html-404-html","errorCode":null,"errorMessage":"fallback must be 'auto', 'index.html', '404.html', or None","messagePattern":"fallback must be 'auto', 'index\\.html', '404\\.html', or None","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"fastapi/routing.py","lineNumber":2057,"sourceCode":"    for media_type, quality in _iter_accept_media_types(\n        request.headers.get(\"accept\", \"\")\n    ):\n        if media_type in {\"text/html\", \"application/xhtml+xml\"} and quality != 0:\n            return True\n    return False\n\n\nclass _FrontendRoute(BaseRoute):\n    def __init__(\n        self,\n        path: str,\n        *,\n        directory: str | os.PathLike[str],\n        fallback: Literal[\"auto\", \"index.html\", \"404.html\"] | None = \"auto\",\n        check_dir: bool,\n    ) -> None:\n        if fallback not in {\"auto\", \"index.html\", \"404.html\", None}:\n            raise AssertionError(\n                \"fallback must be 'auto', 'index.html', '404.html', or None\"\n            )\n        self.path = _normalize_frontend_path(path)\n        self.methods = {\"GET\", \"HEAD\"}\n        self.app = _FrontendStaticFiles(\n            directory=directory, fallback=fallback, check_dir=check_dir\n        )\n\n    def matches(self, scope: Scope) -> tuple[Match, Scope]:\n        return self.matches_with_path(scope, self.path)\n\n    def matches_with_path(self, scope: Scope, path: str) -> tuple[Match, Scope]:\n        if scope[\"type\"] != \"http\":\n            return Match.NONE, {}\n        frontend_path = self._get_frontend_path(path, get_route_path(scope))\n        if frontend_path is None:\n            return Match.NONE, {}\n        child_scope = {","sourceCodeStart":2039,"sourceCodeEnd":2075,"githubUrl":"https://github.com/tiangolo/fastapi/blob/42a41db11f6882807ac3c057b942178d53b97438/fastapi/routing.py#L2039-L2075","documentation":"`_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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\napp.frontend(\"/\", directory=\"dist\", fallback=\"error.html\")\n\n# after\n# rename your file to dist/404.html, then:\napp.frontend(\"/\", directory=\"dist\", fallback=\"404.html\")","handlingStrategy":"type-guard","validationCode":"from typing import Literal\n\n_ALLOWED = {\"auto\", \"index.html\", \"404.html\", None}\n\ndef coerce_fallback(value: str | None) -> Literal[\"auto\", \"index.html\", \"404.html\"] | None:\n    if value not in _ALLOWED:\n        raise ValueError(\n            f\"fallback must be one of {sorted(v for v in _ALLOWED if v)}, got {value!r}\"\n        )\n    return value  # type: ignore[return-value]\n\n# usage\napp.frontend(\"/\", directory=\"dist\", fallback=coerce_fallback(cfg.get(\"FALLBACK\", \"auto\")))","typeGuard":"from typing import Literal\n\ndef is_allowed_fallback(value: object) -> bool:\n    return value in {\"auto\", \"index.html\", \"404.html\", None}\n\n# use as a type guard:\ndef to_fallback(value: object) -> Literal[\"auto\", \"index.html\", \"404.html\"] | None:\n    if not is_allowed_fallback(value):\n        raise TypeError(f\"unsupported fallback: {value!r}\")\n    return value  # type: ignore[return-value]","tryCatchPattern":null,"preventionTips":["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."],"tags":["frontend","configuration","validation"],"analyzedSha":"42a41db11f6882807ac3c057b942178d53b97438","analyzedAt":"2026-08-04T19:23:32.007Z","schemaVersion":2}