Fix #296: reject backend venvs missing uvicorn before launch

Reported by @f3n3k on Windows native install path. Symptom:

    C:\001\backend\venv\Scripts\python.exe: No module named uvicorn
    [backend] exited with 1
    ShadowBroker has stopped. Exit code: 1

Root cause
----------
The Windows Start.bat flow chains:

    Start.bat
      └─ scripts\run-windows-runtime.ps1
           └─ frontend\scripts\dev-all.cjs
                └─ start-backend.js
                     └─ backend\venv\Scripts\python.exe -m uvicorn main:app

`start-backend.js` decided whether an existing `backend\venv` was usable
by calling `canRun(candidate, ["-V"])`. That only checks whether Python
itself can run — it does NOT check whether the backend's actual runtime
dependencies are installed.

When the venv exists but `pip install` never finished (partial install,
failed network, interrupted bootstrap, etc.), the launcher happily
accepted that broken venv, then died with the exact error f3n3k
reported.

Fix
---
New `canRunBackendPython()` helper that requires BOTH:

    python -V                                # Python is runnable
    python -c "import fastapi, uvicorn"      # backend deps are installed

Used in two call sites:

  * `ensureBackendVenv()` — when iterating candidate venvs on first
    launch, reject any venv whose Python can't import the backend's
    real entry-point deps. The launcher then falls through to its
    existing rebuild path (`rebuildBackendVenv`) which reinstalls deps
    before declaring the venv healthy.
  * `rebuildBackendVenv()` — after a rebuild attempt, verify the deps
    are present before returning the new interpreter path. Catches
    silent partial rebuilds.

The check is the import that uvicorn itself would do at startup, so a
green return here genuinely means "uvicorn will start". Cost is one
extra `python -c` per venv candidate on launcher startup — milliseconds.

Verified locally with `node --check start-backend.js`.

Credit: @f3n3k for the original report.
This commit is contained in:
BigBodyCobain
2026-05-22 18:50:27 -06:00
parent 2da739c9e8
commit 9ef02dd06f
+9 -2
View File
@@ -76,6 +76,13 @@ function canRun(command, args) {
return !result.error && result.status === 0;
}
function canRunBackendPython(pythonBin) {
return (
canRun(pythonBin, ["-V"]) &&
canRun(pythonBin, ["-c", "import fastapi, uvicorn"])
);
}
function findBasePython() {
const candidates = isWindows
? [
@@ -135,12 +142,12 @@ function rebuildBackendVenv(targetDir, basePython) {
if (result.error || result.status !== 0) {
return null;
}
return canRun(repairedBin, ["-V"]) ? repairedBin : null;
return canRunBackendPython(repairedBin) ? repairedBin : null;
}
function ensureBackendVenv() {
for (const candidate of venvCandidates) {
if (fs.existsSync(candidate) && canRun(candidate, ["-V"])) {
if (fs.existsSync(candidate) && canRunBackendPython(candidate)) {
persistSelectedVenv(candidate);
return candidate;
}