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

Raised by `_FrontendRoute.__init__` (fastapi/routing.py:2057) as an AssertionError when the `fallback` argument is not one of the allowed values {'auto', 'index.html', '404.html', None}. FastAPI restricts fallback to a fixed literal set so that fallback semantics are well-defined; any other string (e.g. 'main.html', 'true', 0) is rejected at route setup time.

Solutions

  1. Use one of the allowed literals: `fallback='auto'` (default), `'index.html'`, `'404.html'`, or `None`.
  2. If you need a custom file, place it as index.html/404.html in the directory and select the matching literal.
  3. Validate config-driven values against the allowed set before calling `frontend`.

Example fix

// before
router.frontend('/', directory='dist', fallback='main.html')
// after
router.frontend('/', directory='dist', fallback='index.html')
Defensive patterns

Strategy: validation

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 {ALLOWED}')
    return value  # type: ignore[return-value]

router.frontend('/', directory='dist', fallback=coerce_fallback(cfg.get('fallback')))

Type guard

def is_allowed_fallback(value: object) -> bool:
    return value in {'auto', 'index.html', '404.html', None}

Prevention

When it happens

Trigger: Calling `router.frontend('/', directory='dist', fallback='main.html')`. Passing a boolean or integer instead of the literal (`fallback=True`). Passing a typo like `fallback='Auto'` (capitalized). Passing an arbitrary filename expecting it to be used as fallback.

Common situations: Assuming any filename can be a fallback. Copying example code that used a non-standard value. Case-sensitivity mistakes. Passing a value read from config without validating it against the allowed set.

Related errors


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

Appendix: 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 3e8d1526d8)