Skip to content

venv fails with confusing ENOENT when existing venv has broken symlinks #143768

Description

@claydugo

Bug report

Bug description:

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.

Reproducer

mkdir -p oldvenv/bin
ln -s /nonexistent/python3 oldvenv/bin/python3
python3 -m venv oldvenv

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

Activity

  1. added a commit that references this issue on Jan 13, 2026
  2. added
    stdlibStandard Library Python modules in the Lib/ directory
    topic-venvRelated to the venv module
    on Jan 13, 2026
  3. brettcannon commented on Jan 17, 2026

    @brettcannon
    Member

    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).

  4. claydugo commented on Jan 19, 2026

    @claydugo
    ContributorAuthor

    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.

  5. brettcannon commented on Jan 21, 2026

    @brettcannon
    Member

    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).

  6. gaborbernat commented on Jun 5, 2026

    @gaborbernat
    Contributor

    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.

  7. added a commit that references this issue on Jun 5, 2026
  8. added a commit that references this issue on Jun 5, 2026
  9. brettcannon commented on Jun 5, 2026

    @brettcannon
    Member

    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.

  10. claydugo commented on Jun 6, 2026

    @claydugo
    ContributorAuthor

    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

  11. brettcannon commented on Jun 8, 2026

    @brettcannon
    Member

    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).

  12. self-assigned this
    on Jun 8, 2026
  13. added a commit that references this issue on Aug 7, 2026
  14. added 2 commits that reference this issue on Aug 28, 2026
  15. added a commit that references this issue on Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

stdlibStandard Library Python modules in the Lib/ directorytopic-venvRelated to the venv moduletype-bugAn unexpected behavior, bug, or error

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions