Repository navigation
Vendored uv: vendor --revert / remove / rollback delete the vendored wheel while a uv export-ed requirements.txt or pylock.toml still points at it (exit 0), so installs from the exported file fail #996
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:uvuvuv
on Oct 7, 2026 mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actions[agent] More information (same run, main
9c43dfc, uv 0.12.23): the PEP 723 script lane has the same problem. I vendored as.py+s.py.lock(six==1.16.0), then ranuv export --script s.py --frozen -o requirements.txt, which references./.socket/vendor/pypi/<uuid>/six-…whl.vendor --revertthen exits 0 (removed), restoress.py/s.py.lockbyte-identically, and deletes.socket/vendor/, leavingrequirements.txtpointing at a missing wheel. So the fix needs to cover the script-lock revert path too, not justrevert_uv.
Generated by Claude Code
mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actions[agent] Triaged as
priority:p1(uv). Not a duplicate. Confirmed on main9c43dfc:revert_pypi_opts(crates/socket-patch-core/src/vendor/pypi.rs) runs the in-use probeunwired_pypi_reference_clauseonly for ledger entries with no wiring. A wired flavor revert (revert_uv, the script-lockrevert_python_locks, and so on) goes straight toremove_tree_and_prunewith no check of the other project files.Shares root cause with #867: a wired PyPI revert deletes
.socket/vendor/pypi/<uuid>/without first probing every Python project file for the uuid dir. Will be fixed together.
Generated by Claude Code
mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actions[agent] Claiming this issue (with #867; 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 5 commits that reference this issue
on Oct 7, 2026 mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actions[agent] Additional shape from the scheduled uv bug-hunt routine (ledger #310): the superseding re-vendor path hits the same missing residual-reference sweep. On main it can't happen yet, because the re-vendor refuses (
pypi_uv_source_already_exists, #742). It appears once PR #943 (the vendored half of #742) lands. I tested #943's head13247c5.# uv project: six==1.16.0, idna==3.7; mock patch server serves six at uuid aaaaaaaa-…01 socket-patch scan --mode vendored --yes --json # wires .socket/vendor/pypi/aaaaaaaa-…01/six-1.16.0-py2.py3-none-any.whl uv export --no-hashes --format requirements-txt -o requirements.txt # names the aaaaaaaa-…01 wheel # the server now offers a superseding patch bbbbbbbb-…09 socket-patch scan --mode vendored --yes --json # exit 0, success # applied; removed vendor_stale_artifact_removed "previous patch uuid's vendored artifact removed" uv pip install -r requirements.txt # error: Distribution not found at: file:///…/.socket/vendor/pypi/aaaaaaaa-…01/six-1.16.0-py2.py3-none-any.whl
uv re-vendor exit pyproject / uv.lock uv sync --lockedexported requirements.txt 0.5.31 0, success, no warning uuid bbbbbbbb-…09✅ patched (gen 2) ❌ names the deleted aaaaaaaa-…01wheel0.8.17 same same ✅ ❌ 0.12.23 same same ✅ ❌ The stale-artifact removal after a superseding re-vendor should get the same reference check (or a warning naming the file) as the fix for the revert / remove / rollback paths above. Otherwise #943 opens a second route to this failure. Linux only. The paths are plain relative file paths, nothing OS-specific.
Generated by Claude Code
mikolalysenko commented
on Oct 7, 2026 CollaboratorAuthorMore actions[agent] Re-checked on main
05ecc6e, which includes #1015 (scan every vendored-write file for vendored references). This still reproduces: #1015 widened the sweep to the registry's vendored-write rows, but a file that onlyuv exportwrote is still not consulted.On uv 0.5.31, 0.8.17 and 0.12.23 (Linux), I ran
scan --mode vendored --yeson asix==1.16.0uv project, then wrote the export asreq.txt,requirements.txtandpylock.toml(uv export --frozen [--format pylock.toml]). After that,vendor --revert --jsonexits 0successwith no warning,.socket/vendor/pypi/is empty, anduv pip install -r req.txtfails withDistribution not found at: file:///…/.socket/vendor/pypi/aaaaaaaa-…/six-1.16.0-py2.py3-none-any.whl. It reproduced twice per version.For contrast, every ledger-less case passes on the same commit: with
.socket/vendor/state.jsondeleted, revert reportsvendor_orphan_still_wiredand keeps the wheel for uv.lock, script locks,pylock.toml,pylock.dev.tomlandpylock.my-env.toml. The gap is only the residual-reference check after a ledger-backed revert.
Generated by Claude Code
- added a commit that references this issue
on Oct 8, 2026
[agent] Found by the scheduled uv bug-hunt routine (ledger #310).
Summary
After a uv project is vendored,
uv export(requirements.txt, or--format pylock.toml) writes the vendored wheel path into the exported file. That's what uv does with a path source:socket-patch already knows this file wires the patch:
vexreportsrequirements.txt: pkg:pypi/six@1.16.0 is wired to Socket patch <uuid>, but uv.lock resolves the same version from elsewhere.But
vendor --revert,remove pkg:pypi/six@1.16.0androllbackrestorepyproject.toml/uv.lockbyte-identically and then delete.socket/vendor/pypi/<uuid>/with no warning,status: success, exit 0. The--dry-runrevert previews the same cleanremoved. The exported file still names the deleted wheel, so every install from it fails.The uv flavor revert (
revert_uv) has no residual-reference check. The Python dispatcher already has a probe,unwired_pypi_reference_clause, that lists the rootrequirements.txt(plus-rincludes),uv.lock,pylock*.tomland*.py.lockand refuses while any of them mentions the uuid dir. But it only runs for ledger entries with no wiring (guard_unwired_pypi_revert). An entry that does have wiring skips it.Impact
A common uv workflow is to keep an exported
requirements.txt/pylock.tomlfor Docker images, Dependabot or a pip-only deploy. After a revert or remove that reports success,uv pip install -r requirements.txt(andpip install -r, anduv pip sync pylock.toml) fails on every machine withDistribution not found at: file:///…/.socket/vendor/pypi/<uuid>/six-1.16.0-py2.py3-none-any.whl. The ledger entry is gone too, sorepaircan't bring the wheel back. The user has to restore it from git or re-export.Repro (Linux, main
9c43dfc, CPython 3.11)This uses the repo's own
tests/prebuilt_common::Serveras the vendoring service (SOCKET_VENDOR_URL) and a staged.socket/manifest.json+ blob, the same ase2e_vendor_pypi_build.rs::stage_patch.socket-patch remove pkg:pypi/six@1.16.0 --yes(eventsvendor_reverted,removed) andsocket-patch rollback --yesgive the same result. So doesuv export --format pylock.toml -o pylock.toml(uv ≥ 0.8) followed byuv pip install -r pylock.toml.Expected vs actual
docs/testing/uv-compatibility.md(Limits): "vendor --revertrefuses to delete a vendored Python wheel whileuv.lock, a PEP 751 lock, a script, orrequirements.txtstill references it…". The guard's own doc comment invendor/pypi.rsgives the reason: deleting the artifact "while uv.lock, the pylock, the script, or requirements.txt still resolve through the vendored wheel — every later--frozen/--offlineinstall fails"..socket/vendor/<eco>/<uuid>dir — a file that no longer references it is warned about and the artifact removed"; the requirements flavor keeps it onvendor_revert_residual_reference.vendor_revert_residual_reference/vendor_artifact_kept), or at least warns. The dry run previews that.OS × version
vendor --revertremoverollbackFirst bad version
Not bisected.
revert_uvhas never swept for references outside its own wiring records.Suspect code
crates/socket-patch-core/src/vendor/pypi_uv.rs:1013-1050: the end ofrevert_uvwrites the pair and returnskept_artifact: falsewithout checking other files for the uuid dir.crates/socket-patch-core/src/vendor/pypi.rs:1596/1412-1440:guard_unwired_pypi_revert→unwired_pypi_reference_clausealready enumerates exactly the right files (rootrequirements.txt+ includes,uv.lock,pylock*.toml,*.py.lock), but it's gated on the entry having no wiring. Running the same probe after a successful wired flavor revert (pypi.rs:~1620, next to thevendor_revert_residual_referencekeep) would cover this.vendor --revert/removedelete the vendored wheel while a-rinclude still points at it (exit 0), so every laterpip install -r requirements.txtfails #867 (pip flavor: the residual sweep inrevert_requirementsmisses-rincludes). This one is the uv flavor, which has no sweep at all, and the trigger is uv's ownuv export.No probe runs: Linux only (see the table).