Skip to content

fix(env): run the packaged sidecar from the env, not the install directory - #4

Open
CSSFrancis wants to merge 1 commit into
mainfrom
fix/sidecar-cwd-outside-install-dir
Open

CSSFrancis wants to merge 1 commit into
mainfrom
fix/sidecar-cwd-outside-install-dir

Conversation

@CSSFrancis

@CSSFrancis CSSFrancis commented Sep 17, 2026

Copy link
Copy Markdown
Member

Root cause of a Windows auto-update dead end reported against SpyDE (directelectron/spyde#147, still reproducing on 0.5.0 → 0.5.1):

SpyDE cannot be closed. Please close it manually and click Retry.

"The uninstall works fine."

The bug

resolvePythonEnv gave the packaged sidecar cwd: bundledProject<install>/resources/python.

A working directory is an open handle on that directory, and every process the sidecar spawns inherits it. So a surviving backend pins the app's install directory open, and Windows will not let the installer remove it.

The part that made this hard to see: the installer's app-running check can only match processes by their executable path, and the interpreter is <userData>/python-env/Scripts/python.exe — nowhere near the install directory. So the holder was invisible to the very check meant to clear the way. The installer reported the directory clear, the old version could not be removed, and the update dead-ended with nothing obviously running.

"The uninstall works fine" is the tell: a standalone uninstall happens with no app and no sidecar, so nothing holds the directory.

The fix

Run from envDir. Nothing wanted the project directory — the app is installed into the env as a wheel, and what it reads from the bundle it reads by absolute path.

The comment on cwd in backendProcess.ts was also describing only the dev branch (uv run finding pyproject.toml); it now says what each mode passes.

Tests

Two added to pythonEnv.test.ts, alongside the existing findUv coverage:

  • the packaged cwd is not under resourcesPath — the contract itself, stated as the thing that must never regress
  • the packaged spawn is the env interpreter running from the env

Both build a packaged layout whose stamp file already matches the lock, so resolvePythonEnv answers from disk without shelling out to uv.

I checked they actually catch it: reverted to cwd: bundledProject and both fail; restored and both pass.

npm run test:unit   90 tests, 87 pass, 0 fail
npm run typecheck   clean

Note for consumers

This is the root-cause half. The app-side half only ever helps when an update goes through the in-app "Restart to install" — an installer run by hand, or an orphan left by an earlier crash, is the installer's problem. SpyDE's installer macro is being widened in parallel to sweep $APPDATA\*\python-env\* (directelectron/spyde#173), which is what reaps those.

Reaching a SpyDE user needs a de-shell release plus a lock bump there.

A working directory is an open handle, and every process the sidecar
spawns inherits it. Rooted at <install>/resources/python, that handle is
what kept a Windows update from removing the old version — while staying
invisible to the installer's app-running check, which can only match on
executable path and finds this interpreter in the managed env instead.
So the update dead-ended in "cannot be closed. Please close it manually
and click Retry", with the holder nowhere in sight and a Retry that
re-runs the identical check.

Nothing wanted that working directory: the app is installed into the env
as a wheel, and what it reads from the bundle it reads by absolute path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LABjmRsJFyoiVPfU1hveDU

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant