You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
venv fails with confusing ENOENT when existing venv has broken symlinks #143768
python -m venv aborts with [Errno 2] No such file or directory if the target directory already contains a stale venv whose interpreter symlinks point to aremoved Python install.
Re-running venv on an existing environment shouldrefresh those links instead of failing.
I'm not sure how I feel about this. Is feels like you're trying to upgrade the venv without using --upgrade. Otherwise venvs are not really designed to be repaired in-place which this seems to want (they are meant to be disposable).
I don’t use venv heavily day-to-day, so this may be an edge case rather than a common workflow. That said, I did find the error message confusing, and it took me longer than I would have expected to narrow down the cause.
I did consider only improving the error message, but my personal preference is that errors which effectively say “rerun the command with a different flag” place unnecessary burden back on the user.
The “rebuild” wording in my proposal may not be quite right. My intent was to treat broken interpreter symlinks as disposable artifacts. venv already recreates these on a clean run, and they don’t represent user state that needs to be preserved.
this may be an edge case rather than a common workflow.
I think it is an edge case.
That said, I did find the error message confusing, and it took me longer than I would have expected to narrow down the cause.
That's fair, but the tricky bit is that the venv command is a thin wrapper around a library where the exception makes more sense (it's a constant point of contention in the code).
Wearing my virtualenv maintainer hat: re-running on an existing directory is a core workflow there, so I looked at how the two tools compare and what the right fix is.
I'd reframe the question. The thread treats this as "should venv repair environments," and on that framing I agree venvs are disposable. Two things make this a bug rather than a repair feature:
The default symlinks mode returns success for a broken result.python -m venv over a directory with a dangling interpreter link exits 0 and leaves the broken link in place. venv reports success and produces an environment that does not work. No "venvs are disposable" argument justifies a zero exit code for that outcome. This part is worth fixing on its own: venv should not claim success while producing a broken venv.
venv is already inconsistent with itself. It overwrites a regular python3 file on re-run without --clear; that is existing, intended behavior. It diverges only for a dangling symlink, and only as an artifact of symlink-following existence checks. No principled reason explains why a stale file is replaced while a stale link crashes (--copies) or is kept without replacement (symlinks).
I reproduced both modes on current main:
--copies: FileNotFoundError [Errno 2], because the copy follows the dead link.
default symlinks: exits 0 and leaves the broken link in place.
For cross-tool context, virtualenv removes and rewrites the interpreter references on every run: ensure_safe_to_do unlinks the destination before each symlink or copy, so refresh-on-rerun is the established expectation. The latest virtualenv (21.4.1) still fails this exact case with FileExistsError, because ensure_safe_to_do guards on Path.exists(), which returns False for a broken symlink and skips the unlink. Both codebases share one root cause: an existence check that follows symlinks. I'll keep virtualenv aligned with whatever scope we land here.
gh-143770 is scoped correctly. os.path.islink(dst) and not os.path.exists(dst) matches only dangling symlinks, which hold no user data, and leaves valid files and links untouched. It does not refresh stale-but-valid links, so it stays out of --upgrade's territory and adds no repair semantics. I verified it resolves both failures on main and that its test passes.
Activity
pythongh-143768: rebuild broken interpreter symlinks
brettcannon commentedon Jan 17, 2026
I'm not sure how I feel about this. Is feels like you're trying to upgrade the venv without using
--upgrade. Otherwise venvs are not really designed to be repaired in-place which this seems to want (they are meant to be disposable).claydugo commentedon Jan 19, 2026
I don’t use venv heavily day-to-day, so this may be an edge case rather than a common workflow. That said, I did find the error message confusing, and it took me longer than I would have expected to narrow down the cause.
I did consider only improving the error message, but my personal preference is that errors which effectively say “rerun the command with a different flag” place unnecessary burden back on the user.
The “rebuild” wording in my proposal may not be quite right. My intent was to treat broken interpreter symlinks as disposable artifacts. venv already recreates these on a clean run, and they don’t represent user state that needs to be preserved.
brettcannon commentedon Jan 21, 2026
I think it is an edge case.
That's fair, but the tricky bit is that the
venvcommand is a thin wrapper around a library where the exception makes more sense (it's a constant point of contention in the code).gaborbernat commentedon Jun 5, 2026
Wearing my virtualenv maintainer hat: re-running on an existing directory is a core workflow there, so I looked at how the two tools compare and what the right fix is.
I'd reframe the question. The thread treats this as "should venv repair environments," and on that framing I agree venvs are disposable. Two things make this a bug rather than a repair feature:
The default symlinks mode returns success for a broken result.
python -m venvover a directory with a dangling interpreter link exits 0 and leaves the broken link in place. venv reports success and produces an environment that does not work. No "venvs are disposable" argument justifies a zero exit code for that outcome. This part is worth fixing on its own: venv should not claim success while producing a broken venv.venv is already inconsistent with itself. It overwrites a regular
python3file on re-run without--clear; that is existing, intended behavior. It diverges only for a dangling symlink, and only as an artifact of symlink-following existence checks. No principled reason explains why a stale file is replaced while a stale link crashes (--copies) or is kept without replacement (symlinks).I reproduced both modes on current
main:--copies:FileNotFoundError [Errno 2], because the copy follows the dead link.For cross-tool context, virtualenv removes and rewrites the interpreter references on every run:
ensure_safe_to_dounlinks the destination before each symlink or copy, so refresh-on-rerun is the established expectation. The latest virtualenv (21.4.1) still fails this exact case withFileExistsError, becauseensure_safe_to_doguards onPath.exists(), which returnsFalsefor a broken symlink and skips the unlink. Both codebases share one root cause: an existence check that follows symlinks. I'll keep virtualenv aligned with whatever scope we land here.gh-143770 is scoped correctly.
os.path.islink(dst) and not os.path.exists(dst)matches only dangling symlinks, which hold no user data, and leaves valid files and links untouched. It does not refresh stale-but-valid links, so it stays out of--upgrade's territory and adds no repair semantics. I verified it resolves both failures onmainand that its test passes.Merge branch 'main' into pythongh-143768-venv-symlink
Merge branch 'main' into pythongh-143768-venv-symlink
brettcannon commentedon Jun 5, 2026
I agree that's wrong.
That's fair.
Let's see if @claydugo can get a PR up which isn't in a stuck state and I will review it.
claydugo commentedon Jun 6, 2026
Not sure how the pinging works on Github, I feel like linked PRs don't provide a notification.
Sorry if you did already get a notification but I did get a PR green at #150985
brettcannon commentedon Jun 8, 2026
You're right that they don't.
Great! I've added myself to that PR (FYI I am behind on reviews, so I can't promise when I will get to it).
Merge branch 'main' into pythongh-143768-venv-symlink-v2
Merge branch 'main' into pythongh-143768-venv-symlink-v2
gh-143768: Replace a dangling interpreter symlink when creating a venv (
pythongh-143768: Replace a dangling interpreter symlink when creating…