tiangolo/fastapi · error · RuntimeError

Frontend fallback file

Error message

Frontend fallback file '{fallback}' does not exist in directory '{directory}'. Resolved absolute directory: '{resolved_absolute_directory}'

What it means

Raised by `_FrontendStaticFiles._check_fallback_file` (fastapi/routing.py:1925) at app-construction time when `check_dir` is True and the fallback file ('index.html' or '404.html') does not exist inside the frontend directory or is not a regular file. FastAPI guarantees the configured fallback exists at startup so SPA/404 behavior cannot silently break later. The resolved absolute directory is included for diagnostics.

Solutions

  1. Ensure the build emits the chosen fallback file in the directory (add an index.html / 404.html).
  2. Switch to `fallback='auto'` which does not require a specific file at startup.
  3. Set `fallback=None` if you do not want SPA/404 fallback behavior.
  4. Point `directory` at the correct build output folder containing the HTML.

Example fix

// before
router.frontend('/', directory='dist', fallback='404.html')  # no 404.html
// after
router.frontend('/', directory='dist', fallback='auto')
Defensive patterns

Strategy: validation

Validate before calling

import os, pathlib

def ensure_fallback(directory: str, fallback: str) -> None:
    p = pathlib.Path(directory) / fallback
    if not (p.is_file()):
        raise FileNotFoundError(f'Missing fallback file: {p}')

ensure_fallback('dist', 'index.html')
router.frontend('/', directory='dist', fallback='index.html')

Type guard

def fallback_file_ok(directory: object, fallback: str) -> bool:
    import os, pathlib
    if not isinstance(directory, (str, os.PathLike)):
        return False
    return (pathlib.Path(directory) / fallback).is_file()

Prevention

When it happens

Trigger: Calling `router.frontend('/', directory='dist', fallback='index.html')` when 'dist' lacks 'index.html'. Setting `fallback='404.html'` without providing a 404.html in the build output. The fallback file exists but is a directory or symlink to a non-file.

Common situations: Frontend build outputs to a different filename (e.g. '200.html' for some SPA tools) instead of 'index.html'. Partial/corrupted build that emitted assets but not the entry HTML. Misconfigured `fallback` value chosen by copying from another project that used a different build setup.

Related errors


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

Appendix: source

Thrown at fastapi/routing.py:1925

        self.fallback = fallback
        if check_dir and not os.path.isdir(directory):
            raise RuntimeError(
                f"Frontend directory '{directory}' does not exist. "
                f"Resolved absolute path: '{_get_resolved_absolute_path(directory)}'"
            )
        super().__init__(
            directory=directory,
            html=True,
            check_dir=check_dir,
            follow_symlink=False,
        )
        if check_dir and fallback in {"index.html", "404.html"}:
            self._check_fallback_file(fallback)

    def _check_fallback_file(self, fallback: str) -> None:
        _, stat_result = self.lookup_path(fallback)
        if stat_result is None or not stat.S_ISREG(stat_result.st_mode):
            raise RuntimeError(
                f"Frontend fallback file '{fallback}' does not exist in "
                f"directory '{self.directory}'. Resolved absolute directory: "
                f"'{self._get_resolved_directory()}'"
            )

    def _get_resolved_directory(self) -> str:
        assert self.directory is not None
        return _get_resolved_absolute_path(self.directory)

    def get_path(self, scope: Scope) -> str:
        path = _get_fastapi_scope(scope).get(_FASTAPI_FRONTEND_PATH_KEY, "")
        assert isinstance(path, str)
        return os.path.normpath(os.path.join(*path.split("/")))

    async def get_response_for_scope(self, scope: Scope) -> Response:
        if not self.config_checked:
            await self.check_config()
            self.config_checked = True

View on GitHub (pinned to 3e8d1526d8)