[agent] Found by the scheduled uv bug-hunt routine (ledger #310).
Summary
On a vendored uv project, socket-patch repair restores a deleted .socket/vendor/pypi/<uuid>/<wheel> fine on a lock-only checkout: it fetches the pristine wheel from the ledger's registry fragment and reports rebuilt. On the normal developer checkout, where uv sync has already run, it fails. In that case the local build path (crates/socket-patch-core/src/vendor/pypi.rs:1824, "Local build from the installed dist") is taken first. The installed dist is the patched, uv-installed copy of the vendored wheel (uv's INSTALLER, direct_url.json, rewritten RECORD), so the rebuilt wheel never reproduces the sha256 uv.lock pins:
vendor_artifact_rebuild_failed: the rebuilt wheel (.socket/vendor/pypi/<uuid>/six-1.16.0-py2.py3-none-any.whl, sha256 a1b9797e…) does not match the wheel the lockfile still pins (…, sha256 01663f71…); run `socket-patch vendor --revert` for pkg:pypi/six@1.16.0 and re-vendor to re-wire the lockfile
The rebuilt sha is different on every run (a1b9797e…, 794ad96b…, fa13f665…, a2900bcf…, 071be102…), so retrying never helps. The run also removes the package's committed socket-patch.vendor.json sidecar (the documented "nothing kept" path). uv sync --locked then fails with failed to open file …six-1.16.0-py2.py3-none-any.whl.
Impact
The README describes repair as the command that "rebuilds missing/corrupt vendored artifacts". Here it only works if the user first deletes their virtualenv, which nothing tells them to do. The error points them to vendor --revert + re-vendor, which rewrites the lock unnecessarily. The case is common: any checkout where the wheel went missing (bad merge, LFS or .gitignore mishap) after uv sync.
Repro (Linux, uv 0.12.21)
Local mock patch API serving a patched six-1.16.0 wheel, as in the uv VEX e2e harness.
SP="socket-patch --api-url http://127.0.0.1:18080 --api-token t --org test-org"
mkdir proj && cd proj
printf '[project]\nname = "uvp"\nversion = "0.1.0"\nrequires-python = ">=3.9"\ndependencies = ["six==1.16.0"]\n' > pyproject.toml
printf '.venv\n' > .gitignore
uv lock && uv sync
$SP scan --mode vendored --json --yes # applied: 1
git init -q && git add -A && git commit -qm vendored
cd .. && git clone -q proj clone && cd clone
uv sync --locked # installs the patched six.py ✔
rm .socket/vendor/pypi/*/six-1.16.0-py2.py3-none-any.whl
$SP repair --json # exit 1, failed vendor_artifact_rebuild_failed
uv sync --locked # fails: wheel file missing
# Control: the same steps WITHOUT the `uv sync --locked` in the clone -> repair "rebuilt", exit 0, --locked installs the patch.
Expected vs actual
- Expected:
repair rebuilds the committed wheel byte-for-byte, as it already does on a lock-only checkout. The ledger's wiring[].original fragment names the pristine PyPI wheel and its hash, so a verifiable pristine source exists. An installed copy that is itself the vendored (patched) wheel is not a pristine source and shouldn't be preferred over it.
- Actual:
vendor_artifact_rebuild_failed on every run, a non-deterministic rebuilt hash, and the vendor sidecar deleted.
OS × uv matrix (main f6b7fb9)
| OS |
uv 0.2.37, venv present |
uv 0.12.21, venv present |
uv 0.2.37, lock-only (control) |
uv 0.12.21, lock-only (control) |
| Linux |
pass |
fail (3/3 runs) |
pass |
pass |
| macOS (arm64) |
pass |
fail |
pass |
pass |
| Windows |
fail |
fail |
fail¹ |
pass |
¹ Windows + uv 0.2.37 fails even lock-only. The rebuilt wheel (01663f71…) matches what Linux vendoring produces, but not the wheel Windows vendoring committed. That suggests the original scan --mode vendored on that cell was also built from the installed dist, and so isn't reproducible from the pristine wheel. It's likely the same root cause, but I haven't isolated it.
First bad
Not bisected. The published 4.0.0 predates the current uv vendoring backend (#238 / #239).
Suspect code
Probe run
https://github.com/SocketDev/socket-patch/actions/runs/36776160780 (ubuntu / macos / windows × uv 0.2.37 and 0.12.21, lockOnly and withPatchedVenv cases)
[agent] Found by the scheduled uv bug-hunt routine (ledger #310).
Summary
On a vendored uv project,
socket-patch repairrestores a deleted.socket/vendor/pypi/<uuid>/<wheel>fine on a lock-only checkout: it fetches the pristine wheel from the ledger's registry fragment and reportsrebuilt. On the normal developer checkout, whereuv synchas already run, it fails. In that case the local build path (crates/socket-patch-core/src/vendor/pypi.rs:1824, "Local build from the installed dist") is taken first. The installed dist is the patched, uv-installed copy of the vendored wheel (uv'sINSTALLER,direct_url.json, rewrittenRECORD), so the rebuilt wheel never reproduces the sha256uv.lockpins:The rebuilt sha is different on every run (
a1b9797e…,794ad96b…,fa13f665…,a2900bcf…,071be102…), so retrying never helps. The run also removes the package's committedsocket-patch.vendor.jsonsidecar (the documented "nothing kept" path).uv sync --lockedthen fails withfailed to open file …six-1.16.0-py2.py3-none-any.whl.Impact
The README describes
repairas the command that "rebuilds missing/corrupt vendored artifacts". Here it only works if the user first deletes their virtualenv, which nothing tells them to do. The error points them tovendor --revert+ re-vendor, which rewrites the lock unnecessarily. The case is common: any checkout where the wheel went missing (bad merge, LFS or.gitignoremishap) afteruv sync.Repro (Linux, uv 0.12.21)
Local mock patch API serving a patched
six-1.16.0wheel, as in the uv VEX e2e harness.Expected vs actual
repairrebuilds the committed wheel byte-for-byte, as it already does on a lock-only checkout. The ledger'swiring[].originalfragment names the pristine PyPI wheel and its hash, so a verifiable pristine source exists. An installed copy that is itself the vendored (patched) wheel is not a pristine source and shouldn't be preferred over it.vendor_artifact_rebuild_failedon every run, a non-deterministic rebuilt hash, and the vendor sidecar deleted.OS × uv matrix (main
f6b7fb9)¹ Windows + uv 0.2.37 fails even lock-only. The rebuilt wheel (
01663f71…) matches what Linux vendoring produces, but not the wheel Windows vendoring committed. That suggests the originalscan --mode vendoredon that cell was also built from the installed dist, and so isn't reproducible from the pristine wheel. It's likely the same root cause, but I haven't isolated it.First bad
Not bisected. The published 4.0.0 predates the current uv vendoring backend (#238 / #239).
Suspect code
crates/socket-patch-core/src/vendor/pypi.rs:1824: the local build from the installed dist runs whenever a site-packages copy exists, with no check that the installed copy is the pristine registry artifact rather than the vendored wheel.crates/socket-patch-cli/src/commands/repair_vendor.rs:1682: thevendor_artifact_rebuild_failedexit, which also drops the vendor dir.Probe run
https://github.com/SocketDev/socket-patch/actions/runs/36776160780 (ubuntu / macos / windows × uv 0.2.37 and 0.12.21,
lockOnlyandwithPatchedVenvcases)