You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
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
[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: vex reports requirements.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.0 and rollback restore pyproject.toml / uv.lock byte-identically and then delete .socket/vendor/pypi/<uuid>/ with no warning, status: success, exit 0. The --dry-run revert previews the same clean removed. 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 root requirements.txt (plus -r includes), uv.lock, pylock*.toml and *.py.lock and 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.toml for Docker images, Dependabot or a pip-only deploy. After a revert or remove that reports success, uv pip install -r requirements.txt (and pip install -r, and uv pip sync pylock.toml) fails on every machine with Distribution not found at: file:///…/.socket/vendor/pypi/<uuid>/six-1.16.0-py2.py3-none-any.whl. The ledger entry is gone too, so repair can'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::Server as the vendoring service (SOCKET_VENDOR_URL) and a staged .socket/manifest.json + blob, the same as e2e_vendor_pypi_build.rs::stage_patch.
printf'[project]\nname = "app"\nversion = "0.1.0"\nrequires-python = ">=3.9"\ndependencies = ["six==1.16.0", "certifi"]\n'> pyproject.toml
uv lock && uv sync
# stage .socket/manifest.json + blob for pkg:pypi/six@1.16.0 (six.py + marker)
socket-patch vendor --json # exit 0, applied 1
uv export --frozen --no-emit-project -o requirements.txt # six line = ./.socket/vendor/pypi/<uuid>/six-…whl --hash=…
uv venv fresh && uv pip install -p fresh/bin/python -r requirements.txt # control: installs the patched six (SOCKET_PATCHED == 1)
socket-patch vendor --revert --json # exit 0, status success, events: [removed]; no warning# pyproject.toml + uv.lock byte-identical to pre-vendor; .socket/vendor/ deleted
grep socket/vendor requirements.txt # still ./.socket/vendor/pypi/<uuid>/six-1.16.0-py2.py3-none-any.whl
uv venv fresh2 && uv pip install -p fresh2/bin/python -r requirements.txt
# error: Distribution not found at: file:///…/.socket/vendor/pypi/<uuid>/six-1.16.0-py2.py3-none-any.whl
socket-patch remove pkg:pypi/six@1.16.0 --yes (events vendor_reverted, removed) and socket-patch rollback --yes give the same result. So does uv export --format pylock.toml -o pylock.toml (uv ≥ 0.8) followed by uv pip install -r pylock.toml.
Expected vs actual
docs/testing/uv-compatibility.md (Limits): "vendor --revert refuses to delete a vendored Python wheel while uv.lock, a PEP 751 lock, a script, or requirements.txt still references it…". The guard's own doc comment in vendor/pypi.rs gives the reason: deleting the artifact "while uv.lock, the pylock, the script, or requirements.txt still resolve through the vendored wheel — every later --frozen / --offline install fails".
CLI_CONTRACT (vendor revert): composer / maven / nuget "keep the artifact exactly while the live … still names its .socket/vendor/<eco>/<uuid> dir — a file that no longer references it is warned about and the artifact removed"; the requirements flavor keeps it on vendor_revert_residual_reference.
Expected: after restoring the uv pair, the revert keeps the artifact and the entry while another project file still references the uuid dir, with a warning naming the file (e.g. vendor_revert_residual_reference / vendor_artifact_kept), or at least warns. The dry run previews that.
Actual: the artifact is deleted silently, exit 0, and the exported file is left pointing at nothing.
OS × version
OS
uv
exported file
vendor --revert
remove
rollback
Linux
0.5.31
requirements.txt
reproduces
reproduces
reproduces
Linux
0.8.17
requirements.txt / pylock.toml
reproduces / reproduces
–
–
Linux
0.12.23
requirements.txt (×2) / pylock.toml
reproduces / reproduces
reproduces
reproduces
macOS / Windows
–
not probed: the missing check is in pure Rust revert logic with no OS-specific path, and uv fails on a missing local wheel on every OS
First bad version
Not bisected. revert_uv has never swept for references outside its own wiring records.
Suspect code
crates/socket-patch-core/src/vendor/pypi_uv.rs:1013-1050: the end of revert_uv writes the pair and returns kept_artifact: false without 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_clause already enumerates exactly the right files (root requirements.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 the vendor_revert_residual_reference keep) would cover this.
[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).