Skip to content

Pipenv stale-install remedy can't replace a base-interpreter copy in a --site-packages venv: pip uninstall refuses it and pipenv sync keeps it, so following the warning leaves the package unpatched #1450

Closed

Description

[agent] Found by the scheduled Pipenv bug-hunt routine (ledger #313).

Summary

Since #1419 (#409), the hosted and vendored stale-install warnings correctly fire when a pipenv --site-packages / PIPENV_SITE_PACKAGES=1 venv imports the upstream release from its base interpreter (e.g. Debian's /usr/lib/python3/dist-packages/six.py). But the warning still prints the generic Pipenv remedy from stale_install_remedy, and neither command it names works for a base copy:

  1. pipenv run pip uninstall -y six && pipenv sync: pip refuses with Not uninstalling six at /usr/lib/python3/dist-packages, outside environment <venv> / Can't uninstall 'six'. Then pipenv sync sees six 1.16.0 already satisfied by the base copy, says "All dependencies are now up-to-date!", and installs nothing.
  2. pipenv --rm && pipenv sync: with PIPENV_SITE_PACKAGES=1 set (or the user recreating the venv with --site-packages as their project requires), the new venv inherits the same base copy, and sync again installs nothing.

After either remedy the venv still imports upstream six. The next scan prints the same warning and the same remedy, so the user loops. The warning also says "the rewritten Pipfile.lock only protects fresh installs", but a fresh --site-packages venv on the same host also keeps the base copy. The lock protects nothing there.

It fails closed (vex gives not_applied, and the warning re-fires), so nothing is falsely attested. The defect is that the printed remedy, which pipenv-compatibility.md documents as verified, cannot work in this shape.

Impact

Any Pipenv project using --site-packages / PIPENV_SITE_PACKAGES=1 whose base interpreter carries the same release (common with distro python3-* packages and Docker base images). Users follow the remedy, see it fail, and can't reach a patched install without knowing a pip-level workaround.

Repro (Linux, Debian/Ubuntu python3.11 with python3-six 1.16.0 in /usr/lib/python3/dist-packages)

export WORKON_HOME=$PWD/workon PIPENV_SITE_PACKAGES=1
mkdir proj && cd proj
cat > Pipfile <<'EOF'
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
six = "==1.16.0"
EOF
pipenv --python /usr/bin/python3.11 lock && pipenv sync
pipenv run python -c 'import six; print(six.__file__)'   # /usr/lib/python3/dist-packages/six.py
socket-patch scan --mode hosted --yes ...                 # (or --mode vendored); warns: installed files in /usr/lib/python3/dist-packages still differ ...
# remedy 1, verbatim
pipenv run pip uninstall -y six && pipenv sync            # "Can't uninstall 'six'" / "All dependencies are now up-to-date!"
pipenv run python -c 'import six; print(six.__file__)'   # still /usr/lib/python3/dist-packages/six.py (upstream)
# remedy 2, verbatim
pipenv --rm && pipenv sync
pipenv run python -c 'import six; print(six.__file__)'   # still /usr/lib/python3/dist-packages/six.py (upstream)
socket-patch scan --mode hosted ...                       # same warning, same remedy
socket-patch vex --product pkg:pypi/proj@1 -O vex.json    # omits six: not_applied (correct)

I used a local mock patch API that serves a patched six-1.16.0 wheel. The oracle is the first line of the imported six.py.

A command that does work (the base copy stays untouched, and the venv gets the patched wheel):
pipenv run pip install --no-deps --ignore-installed -r <(pipenv requirements), with the lock's category args on pipenv requirements.

Expected vs actual

  • Expected: docs/testing/pipenv-compatibility.md ("Warm virtualenvs are never reinstalled") presents the two printed commands as the verified remedies, and the warning tells the user how to get a patched install. When the stale copy is in a base-interpreter site dir the venv inherits (one of the dirs Fix crawl of --system-site-packages venvs (#409) #1419 added), the remedy should name a command that installs over it inside the venv (e.g. the pip install --no-deps --ignore-installed form above). Otherwise it should say plainly that the base copy shadows the lock, and drop the "only protects fresh installs" claim.
  • Actual: the same generic remedy, which leaves the package unpatched on every attempt.

OS × version

OS Pipenv hosted warning fires remedy 1 (pip uninstall && sync) remedy 2 (--rm && sync, PIPENV_SITE_PACKAGES=1) vex
Linux 2022.12.19 (py3.11) yes fails n/a (Pipenv 2022 recreated on py3.13, harness quirk) —
Linux 2023.12.1 (py3.11) yes fails (2/2) fails not_applied
Linux 2026.8.0 (py3.11) yes (hosted + vendored) fails (2/2) fails not_applied
macOS / Windows any untested (probe branches blocked)

Not a regression. Before #1419 no warning fired at all (#409).

Suspect code

  • crates/socket-patch-core/src/vendor/pypi_pipenv.rs:786 stale_install_remedy: one remedy for every site dir.
  • crates/socket-patch-cli/src/commands/scan/hosted/python.rs:143 (hosted) and crates/socket-patch-core/src/vendor/pypi.rs:780 (vendored) build the warning, already know the site dir (/usr/lib/python3/dist-packages), and could tell a base dir from the venv's own.
  • The same question probably applies to the pip / uv / Poetry remedies for base copies, but I only tested Pipenv.

Tested on main 6ad6add (v5.0.0).

Activity

  1. mikolalysenko commented on Oct 12, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged: priority:p1 (Pipenv / PyPI family). Confirmed on main (6ad6add): both the hosted warning (commands/scan/hosted/python.rs:136) and the vendored one (vendor/pypi.rs:776) call pypi_pipenv::stale_install_remedy with only the lock and the package name, so the remedy never depends on whether the stale dir is the venv's own site-packages or a base-interpreter dir the venv inherits through --site-packages (the dirs #1419 added). Both callers already hold the stale site dir. Not a duplicate: #409/#1419 made the warning fire, this is about what it tells the user to do. No open PR covers it, and it does not share a root cause with #1447 (Poetry 1.3 stamp) or #1445 (uv orphaned hosted wiring).


    Generated by Claude Code

  2. mikolalysenko commented on Oct 12, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: stale_install_remedy prints one Pipenv remedy whatever site dir holds the stale copy). Branch: agent/fix-pipenv-base-site-remedy. Claim-ID: 2026-10-12T23:21:09Z-c2f8d4


    Generated by Claude Code

  3. mikolalysenko commented on Oct 12, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1455


    Generated by Claude Code

  4. added a commit that references this issue on Oct 12, 2026
    0ed865b
  5. added a commit that references this issue on Oct 13, 2026
  6. mikolalysenko commented on Oct 13, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Pipenv bug-hunt re-verification on main aa85c6c (5.0.1), Linux, real Pipenv. I followed the new install-over remedy verbatim, and in every cell the venv then imports the patched six. The re-scan gives no warning, and vex gives not_affected:

    Pipenv mode shape result
    2026.9.1 hosted Debian /usr/lib/python3/dist-packages base, [packages] pass
    2022.12.19, 2023.12.1 hosted [dev-packages] only (--dev) pass
    2022.12.19, 2026.9.1 hosted [docs] only (--categories "docs") pass
    2023.12.1, 2026.9.1 hosted venv's own upstream copy shadowing the base one (shadowing remedy) pass
    2022.12.19, 2023.12.1, 2026.9.1 vendored Debian base, [packages] pass
    2023.12.1, 2026.9.1 hosted + vendored upstream six in the ~/.local user site pass

    One gap: a pin in two or more categories with one of them named prints --categories "packages docs", which Pipenv 2022.12.x–2023.2.4 export as empty. Filed as #1507.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions