[BUG] Dash.run(debug=True, backend="fastapi") reload subprocess silently ignores reload_dirs/reload_excludes kwargs
@T4rk1n is already working on this.
Since Aug 27, 2026.
- Dominant language
- Python
- Stars
- 24.4k
- Forks
- 2.3k
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 13
Description
Summary
When using the FastAPI backend, app.run(host=..., port=..., debug=True, **kwargs) (main-thread, non-Jupyter path) triggers dash/backends/_fastapi.py::FastAPIBackend.run(), which spawns python -m uvicorn <module>:<app>.server --reload as a subprocess. Extra kwargs such as reload_dirs=[...] or reload_excludes=[...] are accepted by Dash.run(..., **flask_run_options) without error and flow straight into FastAPIBackend.run()'s **kwargs — but only kwargs.get("reload") is ever read there; every other reload-related kwarg is silently dropped when building the subprocess's CLI args. The comment directly above the subprocess.Popen(...) call even says:
# Add any other kwargs as CLI args if needed
proc = subprocess.Popen(uvicorn_args, env=env)
...but nothing implements it.
Practical effect: without an explicit --reload-dir, uvicorn/watchfiles fall back to watching the entire current working directory. In any project run with cwd above the app's own package (e.g. a monorepo, or simply python path/to/app/index.py invoked from a parent directory), the reload subprocess ends up recursively watching unrelated directories — including a shared virtualenv (.venv/site-packages/...) — so any unrelated package file being touched triggers a full, spurious app reload.
Reproduction
# repro.py -- run as `python repro.py` from a directory ABOVE this file,
# e.g.:
# mkdir -p repo_root/app_dir && cd repo_root
# (place this file at repo_root/app_dir/repro.py)
# python app_dir/repro.py
from dash import Dash, html
app = Dash(__name__, backend="fastapi")
app.layout = html.Div("hello")
if __name__ == "__main__":
app.run(
host="127.0.0.1",
port=8050,
debug=True,
reload_dirs=["app_dir"],
reload_excludes=["app_dir/tests/*"],
)
- Expected: the spawned
uvicorn --reloadsubprocess only watchesapp_dir/, excludingapp_dir/tests/*. - Actual:
reload_dirs/reload_excludesare silently ignored. The subprocess inherits the parent's cwd (repo_root/) and uvicorn defaultsreload_dirsto[Path.cwd()], so it watchesrepo_root/recursively -- includingrepo_root/.venvif one exists there.
Root cause
dash/backends/_fastapi.py, FastAPIBackend.run() (dash 4.4.1):
def run(self, dash_app: Dash, host, port, debug, **kwargs):
...
uvicorn_args = [
sys.executable, "-m", "uvicorn", app_path,
"--host", str(host), "--port", str(port),
]
if kwargs.get("reload"):
uvicorn_args.append("--reload")
dev_tools = dash_app._dev_tools
config = dict(
{"debug": debug} if debug else {"debug": False},
**{f"dev_tools_{k}": v for k, v in dev_tools.items()},
)
env = os.environ.copy()
env[_ENV_CONFIG] = json.dumps(config)
# Add any other kwargs as CLI args if needed
proc = subprocess.Popen(uvicorn_args, env=env)
proc.wait()
Only kwargs["reload"] is ever consulted. reload_dirs, reload_excludes, reload_includes (and anything else a caller passes through app.run(...)) reach this function but are never translated into --reload-dir / --reload-exclude / --reload-include CLI args for the child process.
Suggested fix
Forward at least the reload-scoping kwargs onto uvicorn_args, e.g.:
for d in kwargs.get("reload_dirs") or []:
uvicorn_args += ["--reload-dir", str(d)]
for p in kwargs.get("reload_excludes") or []:
uvicorn_args += ["--reload-exclude", str(p)]
for p in kwargs.get("reload_includes") or []:
uvicorn_args += ["--reload-include", str(p)]
Since this is silent today (no error, no warning), a caller has no way to discover the gap short of reading this source file directly. At minimum, forwarding these three would close the gap for the common case; more generally it might be worth warning on any kwarg that isn't recognized/forwarded here.
Environment
- dash 4.4.1 (
dash[fastapi],backend="fastapi") - uvicorn 0.52.0
- Windows 11, Python 3.11 (the affected code path is not inside a platform-specific branch, so this should reproduce on any OS)
Related
Possibly adjacent to #3818 / #3912 (uvicorn --reload subprocess handling in the FastAPI backend also has callback double-registration issues there), but this is a distinct symptom -- silently dropped reload-scoping kwargs / overly broad default watch scope, not callback duplication.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.