Repository navigation
Vendored uv script locks: once the user deletes one wired script (or its .py.lock), vendor --revert, remove, rollback and the hosted takeover can never unwind the other scripts, and the takeover's suggested fix is the command that fails #890
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 5, 2026 mikolalysenko commented
on Oct 5, 2026 CollaboratorAuthorMore actions[agent] Probe run https://github.com/SocketDev/socket-patch/actions/runs/37370890534: on Windows × uv 0.12.23, all 12 cells reproduce (deleted
b.py+b.py.lock/ onlyb.py.lock/ onlyb.py, × revert / remove / rollback / takeover).- revert, remove and rollback exit 1 with
cannot read .\\b.py.lock: The system cannot find the file specified.(or.\\b.py). - The takeover exits 0 with
success. a.pystays wired, anduv run --locked --script a.pystill runs the vendored wheel.
The ubuntu and Windows 0.5.31 jobs ran in the same run; their logs are linked there. The macOS jobs were still queued when this run ended.
vendor --revert --dry-runpredicts the failure (exit 1, same message), andscan --mode hosted --dry-runreportsredirect_vendored_revert_failedwithredirected: 0. So the previews are consistent; the wet unwind just never gets past the deleted file.
Generated by Claude Code
- revert, remove and rollback exit 1 with
mikolalysenko commented
on Oct 5, 2026 CollaboratorAuthorMore actions[agent] Triaged as
priority:p1(uv). It's a distinct cause: the vendored unwind of a multi-record PyPI ledger entry treats a wiring record whose file is gone as a hard error, when it should drop that record with an advisory. This is related to, but not the same as, #869 (a stale specifier written back on revert). No duplicate or open PR found.
Generated by Claude Code
mikolalysenko commented
on Oct 6, 2026 CollaboratorAuthorMore actions[agent] macOS evidence from the run-21 probe, which finished after the issue was filed. On main
9c43dfc, macos-latest × uv 0.5.31 and 0.12.23 reproduce all 12 cells: deletingboth,lockorscriptcombined with each ofrevert,remove,rollbackandtakeover.vendor --revertexits 1 (partialFailure),removeexits 1 (error) androllbackexits 1 (partial_failure), all withcannot read ./b.py.lock(or./b.py).a.pystays wired and.socket/vendor/pypistays.scan --mode hosted(the V→H takeover) exits 0 withsuccess, buta.pyis still wired to the vendored wheel.
Job: https://github.com/SocketDev/socket-patch/actions/runs/37370890534/job/111967435815
That makes the OS table Linux ✗, Windows ✗, macOS ✗.
Generated by Claude Code
mikolalysenko commented
on Oct 6, 2026 CollaboratorAuthorMore actions[agent] uv bug-hunt run 22: the same failure also hits the project lane, so this isn't limited to scripts. Repro on main
9c43dfc, Linux, uv 0.8.17 and 0.12.23, reproduced on both versions:- Vendor a uv project.
- The user deletes
uv.lock, for examplerm uv.lockbefore a relock, or a branch that drops it, with pyproject.toml still wired. - Run any unwind. All of them fail:
vendor --revert exit 1 partialFailure "cannot read uv.lock: No such file or directory (os error 2)" remove pkg:pypi/six@1.16.0 exit 1 error "could not revert vendoring for pkg:pypi/six@1.16.0: cannot read uv.lock: …" rollback exit 1 partial_failure "cannot read uv.lock: …" scan --mode hosted exit 0 success redirected 0, skipped vendored_revert_failed: "…could not be reverted (cannot read uv.lock …); NOT switched to hosted — run `socket-patch vendor --revert`"After each of these, pyproject.toml still has
six = { path = ".socket/vendor/pypi/<uuid>/six-1.16.0-…whl" }and.socket/vendorstays, so the user can't remove the patch with any command. The takeover again exits 0 and recommends the revert that fails.Controls, all passing on both versions: restoring only
pyproject.toml(git checkout pyproject.toml) or onlyuv.lockand then running revert / remove / rollback / takeover → success, nothing left wired,.socket/vendorgone.Code:
crates/socket-patch-core/src/vendor/pypi_uv.rs:804returnsRevertOutcome::failed("cannot read uv.lock")before it touches pyproject.toml. The comment atcrates/socket-patch-core/src/vendor/pypi.rs:1570-1575already notes that "flavoruvwith uv.lock gone fails 'cannot read uv.lock'", but it guards onlyrepair-reconstructed entries with empty wiring, not a normal entry whose lock went missing. The fix for the script lane should cover this file as well: unwire what still exists and treat a missing wired file as already reverted.
Generated by Claude Code
[agent] Found by the scheduled uv bug-hunt routine (ledger #310).
Summary
A vendored patch that is wired into several PEP 723 script locks (
a.py+a.py.lock,b.py+b.py.lock, …) records one wiring record per file in a single ledger entry. If the user then deletes one of those scripts, which is a normal thing to do with throwaway uv scripts, every unwind of the whole entry hard-fails on the missing file:Nothing is reverted. The surviving
a.py/a.py.lockstay wired to.socket/vendor/..., and the wheel and ledger entry stay too. The same thing happens withremove, withrollback, and with the vendored → hosted takeover. The takeover exits 0 withsuccess, but its skip reason saysNOT switched to hosted — run \socket-patch vendor --revert` to clean up, which is exactly the command that fails. The CLI offers no way out. The user has to restore the deleted script from git, run the revert, and delete it again, or edit.socket/vendor/state.json` by hand.The PEP 751 lane goes through the same code path: with
pylock.toml+pylock.dev.tomlboth vendored, deletingpylock.dev.tomlmakesvendor --revertfail withcannot read ./pylock.dev.toml.Hosted mode handles the same deletion fine:
rollbackexits 0 and restoresa.py/a.py.lock, because hosted pins live only in the files that still exist.Impact
successwhile leaving everything vendored, and the remedy it prints loops back to the failing command.vendor --checkstays green (exit 0) throughout, so CI doesn't flag the stuck state.Repro (real uv, local mock patch server serving a patched six 1.16.0 wheel)
Expected vs actual
RevertOutcome::failed), for every unwind command, with a raw I/O error.Matrix (main
9c43dfc)The failure comes from a file read, not from anything OS-specific. I'll add the probe results as a comment. I didn't bisect: the behavior looks present since multi-file script / pylock vendoring landed.
Suspect code
crates/socket-patch-core/src/vendor/pypi_lock.rs:770:revert_python_locksreturnsRevertOutcome::failed(error)on anyread_fileerror, includingNotFound, before it looks at the other records.crates/socket-patch-cli/src/commands/scan/hosted.rs:1917: the takeover skip text recommendsvendor --reverteven when that revert is the step that just failed.Backlog review — 2026-10-08
Priority: P1 → P2. Deleting one wired uv script prevents unwinding others. Real recoverability issue with a user-deleted-file trigger; do not close it as noise.