Repository navigation
venv fails with confusing ENOENT when existing venv has broken symlinks #143768
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
on Jan 13, 2026 - added a commit that references this issue
on Jan 13, 2026 - addedstdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-venvRelated to the venv moduleRelated to the venv module
on Jan 13, 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).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
venvcommand is a thin wrapper around a library where the exception makes more sense (it's a constant point of contention in the code).Reacted by Clay DugoWearing 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.- 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_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.Reacted by Clay Dugo- added a commit that references this issue
on Jun 5, 2026 - added a commit that references this issue
on Jun 5, 2026 The default symlinks mode returns success for a broken result.
I agree that's wrong.
venv is already inconsistent with itself.
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.
Reacted by Clay DugoNot 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
Not sure how the pinging works on Github, I feel like linked PRs don't provide a notification.
You're right that they don't.
Sorry if you did already get a notification but I did get a PR green at #150985
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).
Reacted by Clay Dugo- added a commit that references this issue
on Aug 7, 2026
Bug report
Bug description:
python -m venvaborts with[Errno 2] No such file or directoryif 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.
Reproducer
Actual behavior
Traceback ends with
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/oldvenv/bin/python3'during the chmod phase.Expected behavior
venv should replace broken interpreter symlinks just as it overwrites other files when re-run on an existing directory (without requiring
--clear).CPython versions tested on:
3.13
Operating systems tested on:
Linux
Linked PRs