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
- Ensure the build emits the chosen fallback file in the directory (add an index.html / 404.html).
- Switch to `fallback='auto'` which does not require a specific file at startup.
- Set `fallback=None` if you do not want SPA/404 fallback behavior.
- 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
- Prefer fallback='auto' unless you require a specific file.
- Verify the fallback file is emitted by your build config.
- Add a CI check that asserts index.html/404.html exists in the build output.
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
- Frontend directory ' ' does not exist. Resolved absolute…
- A frontend path cannot be empty
- A frontend path must start with '/'
- fallback must be 'auto', 'index.html', '404.html', or None
- No route exists for name
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 = TrueView on GitHub (pinned to 3e8d1526d8)