Repository navigation
Vendored requirements.txt: vendor --revert / remove delete the vendored wheel while a -r include still points at it (exit 0), so every later pip install -r requirements.txt fails #867
Description
Activity
- addedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:pippip / requirements.txtpip / requirements.txt
on Oct 5, 2026 mikolalysenko commented
on Oct 5, 2026 CollaboratorAuthorMore actions[agent] Triaged as
priority:p1(pip). Still present on main0d302dc: the residual-reference sweep at the end ofrevert_requirements(vendor/pypi_requirements.rs,for (file, content) in &reverted) scans only the files named in the ledger's wiring records, never the in-root-rinclude tree thatrequirements_entry_in_usewalks. So a vendor line moved into an include goes undetected, and the artifact is deleted. This isn't a duplicate, and no open PR addresses it yet.
Generated by Claude Code
mikolalysenko commented
on Oct 5, 2026 CollaboratorAuthorMore actions[agent] More information (main
9c43dfc, real pip 26.2.1 / py3.11): the same failure also happens when the vendored line is moved into a sibling file thatrequirements.txtdoes not include. A fix that only walks the-rtree from the root won't cover this case.# after `socket-patch vendor` on `six==1.16.0` L=$(cat requirements.txt) printf 'idna==3.7\n' > requirements.txt printf '%s\n' "$L" > requirements-dev.txt # the vendor line now lives only here socket-patch vendor --check # exit 1: "wiring missing: no lockfile or config references .socket/vendor/pypi/<uuid> any more" socket-patch vendor --revert # exit 0, `vendor_revert_line_drifted`; .socket/vendor/pypi/ is deleted pip install -r requirements-dev.txt # ERROR: [Errno 2] No such file or directory: '…/.socket/vendor/pypi/<uuid>/six-1.16.0-py2.py3-none-any.whl'
remove pkg:pypi/six@1.16.0behaves the same way (exit 0,vendor_reverted+vendor_revert_line_drifted, wheel deleted). Reproduced twice. Ascan --mode vendoredin between re-adds a rootsix (transitive)line, which is fine, but a later revert still deletes the wheel under therequirements-dev.txtreference.A related cosmetic problem shows up in the same output: the drift warning prints a Rust
Debugvalue,requirements.txt: the vendor line for Some("requirements.txt:1") changed since vendoring; left untouched(vendor/pypi_requirements.rsdrift_warning,{:?}ofrec.key). The same pattern is invendor/common.rs:806,pypi_uv.rs:847andpypi_pipenv.rs:468.
Generated by Claude Code
mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actions[agent] Shares root cause with #996: a wired PyPI revert deletes
.socket/vendor/pypi/<uuid>/without first probing every Python project file for the uuid dir. The requirements flavor sweeps only its own recorded files, andrevert_pypi_optsruns the shared probe (unwired_pypi_reference_clause, which already walks the-rinclude tree) only for unwired entries. Will be fixed together.
Generated by Claude Code
mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actions[agent] Claiming this issue (with #996; shared root cause: a wired PyPI vendor revert deletes the vendored wheel without probing every Python project file for the uuid dir). Branch: agent/fix-pypi-revert-residual-probe. Claim-ID: 2026-10-07T09:20:53Z-6884c3
Generated by Claude Code
mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actions- added a commit that references this issue
on Oct 8, 2026
[agent] Found by the scheduled pip / requirements.txt bug-hunt routine (ledger #309).
Summary
revert_requirementshas a guard for vendor lines that drifted: when a vendor line no longer matches its ledger record, the revert leaves it in place, and a residual-reference sweep keeps the.socket/vendor/pypi/<uuid>/artifact so the line still installs (vendor_revert_residual_reference→vendor_artifact_kept/vendor_revert_kept). That works when the drifted line is still in the file the ledger recorded.The sweep only scans the files named in the ledger's wiring records, though. If the user moves the vendored line into a
-rinclude (for example while splittingrequirements.txtintorequirements/base.txt), the revert:requirements.txt:1record asvendor_revert_line_drifted;requirements.txt, which no longer mentions the uuid, so it finds no residual reference;.socket/vendor/pypi/<uuid>/(and all of.socket/) and exits 0 withstatus: success.requirements/base.txtstill carries./.socket/vendor/pypi/<uuid>/six-1.16.0-py2.py3-none-any.whl # socket-patch vendor: six==1.16.0, so pip fails on every install from then on.remove pkg:pypi/six@1.16.0goes through the same path and gives the same result.This is also the remedy socket-patch itself prescribes. In the same layout, a superseding patch's re-vendor fails with
pypi_requirements_already_vendored("requirements/base.txt: already routes six … (requirements.txt: the vendor line changed since vendoring); runsocket-patch vendor --revertbefore re-vendoring"). Following that advice breaks the project.Impact
After a
vendor --revertorremovethat reports success,pip install -r requirements.txtfails on every machine and in CI withOSError: [Errno 2] No such file or directory: '…/.socket/vendor/pypi/<uuid>/six-1.16.0-py2.py3-none-any.whl'. The wheel is gone, and the ledger entry is too, so neithervendor --revertnorrepaircan recover it. The user has to restore it from git or hand-edit the include.Repro (Linux, main
99f61d2, pip 26.2.1 and 24.0 / CPython 3.11)This uses a local mock of the patch API and vendoring service (
batch/by-package/view/<uuid>plusprebuilt_common::mount_view_from_sourceover the real installedsix.py), run through a throwaway driver that wasn't committed.socket-patch remove pkg:pypi/six@1.16.0 --json --yesin the same state gives the same result: exit 0,vendor_revertedplusvendor_revert_line_drifted, and the artifact is deleted.Control (same file): when the vendored line stays in
requirements.txtbut has drifted, because the trailing comment was edited or a space was dropped before#, the revert correctly reportsvendor_revert_residual_reference,vendor_artifact_keptandvendor_revert_kept, and the wheel stays. So the guard exists. It just doesn't look past the recorded files.Expected vs actual
The function's own contract (
pypi_requirements.rs, doc comment ofrevert_requirements): "any surviving reference to the vendored uuid dir afterwards raisesvendor_revert_residual_reference", and the sweep comment: "a leftover line pointing at the (about to be deleted) uuid dir would break installs." The #786 in-use probe (requirements_entry_in_use) already treats the requirements tree as "the rootrequirements.txtplus in-root-rincludes".-rincludes). The moved line keeps the artifact (vendor_revert_residual_reference,vendor_artifact_kept), so pip keeps installing.OS × version
-rinclude →vendor --revertremoveFirst bad version
Not bisected. The sweep has only ever covered
reverted(the recorded files).Suspect code
crates/socket-patch-core/src/vendor/pypi_requirements.rs:462-470: the residual-reference sweep iterates&reverted, which holds only the files named inentry.wiring. It should also scan the rest of the requirements tree (requirements_include_names(root), the same walk the Vendored requirements.txt after the user removes or bumps a vendored pin: the rescan re-adds the removed package as a "(transitive)" line (exit 0), or exits 1 forever after a bump, andscan --prunenever reverts the entry #786 in-use probe uses) before it allows the artifact to be deleted.plan_rewire(pypi_requirements.rs:~697), whose doc comment says "present verbatim in an editable file of the tree" but only looks in the recorded file. That's why the superseding re-vendor sends the user tovendor --revertin the first place.