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 transitive package later added as a direct dependency (uv add six==1.16.0): vendor --revert / remove / rollback half-revert the pair, so uv sync --locked fails and vendor --check claims nothing references the wheel #1374
[agent] Found by the scheduled uv bug-hunt routine (ledger #310).
Summary
Vendor a transitive package in a uv project (here six, pulled in by python-dateutil and held at 1.16.0 by a user constraint-dependencies). Vendored mode wires it through [tool.uv] override-dependencies + [tool.uv.sources] and records five wiring entries: uv_override, uv_sources_entry, uv_lock_package, uv_lock_manifest_overrides and uv_lock_manifest_constraints.
Then the user promotes it to a direct dependency, for example to pin it: uv add six==1.16.0. uv writes a new root requires-dist element, { name = "six", path = ".socket/vendor/pypi/<uuid>/six-1.16.0-…whl" }, because the socket-written source routes it. socket-patch never recorded that element.
vendor --revert, remove six and rollback then revert every recorded fragment. They drop the sources line and the override, and put the lock's [[package]] and [manifest] back on PyPI. They leave the root requires-dist element still pointing at the wheel. Only after writing does the residual-reference guard notice that uv.lock still names the uuid dir, and it keeps the wheel and the ledger entry.
The result is a pair uv rejects:
uv sync --locked fails with "The lockfile at uv.lock needs to be updated".
vendor --revert exits 0. remove and rollback exit 1.
vendor --check then exits 1 with wiring missing: no lockfile or config references .socket/vendor/pypi/<uuid> any more … re-run socket-patch vendor to rewire it. That's false, because uv.lock still references it, and the remedy re-vendors the package the user just asked to unwind.
Impact
A user who pins a vendored transitive dependency directly, which is a normal uv add, can't unwind it cleanly. Every --locked / --frozen-checked CI install breaks after the revert. The only recovery is to know to run uv lock by hand and then vendor --revert again (that converges).
The same uv add after uv remove python-dateutil (the #1287 shape plus a direct re-add) fails the same way, on main and on PR #1337.
Repro
Requires a patch API serving a six@1.16.0 patch. I used the routine's local mock with --api-url/--patch-server-url/--vendor-url http://127.0.0.1:8765, abbreviated sp below.
mkdir demo &&cd demo
cat > pyproject.toml <<'EOF'[project]name = "demo"version = "0.1.0"requires-python = ">=3.9"dependencies = ["attrs>=20"][tool.uv]constraint-dependencies = ["six==1.16.0"]EOF
uv add -q python-dateutil==2.9.0.post0 && uv sync -q
sp scan --mode vendored --yes # six wired via override + sources, exit 0
uv add -q six==1.16.0 # promote to direct; uv.lock root requires-dist now has { name = "six", path = ".socket/vendor/pypi/<uuid>/…" }
uv sync -q --locked # ok, six patched
sp vendor --check;echo$?# 0
sp vendor --revert --json;echo$?# 0: vendor_revert_residual_reference + vendor_artifact_kept + vendor_revert_kept
uv sync -q --locked # error: The lockfile at `uv.lock` needs to be updated
sp vendor --check;echo$?# 1: "wiring missing: no lockfile or config references … re-run socket-patch vendor"
grep -n aaaaaaaa uv.lock # requires-dist = [ …, { name = "six", path = ".socket/vendor/pypi/…/six-1.16.0-py2.py3-none-any.whl" } ]
After the revert, pyproject.toml has no sources or override, and [[package]] six is back on registry = "https://pypi.org/simple". The root [package.metadata] requires-dist still carries the path element.
vendor --check should not claim "no lockfile or config references" the uuid dir while uv.lock does.
Actual: pyproject and four of the five lock fragments are reverted, the root requires-dist path element is left, uv sync --locked fails, and vendor --revert exits 0.
Matrix (Linux, main 85105c9)
unwind
uv 0.5.31
uv 0.12.24
vendor --revert (parent kept, uv add six==1.16.0)
fail: exit 0, --locked fails, check 1
fail (same)
remove six --yes
fail: exit 1, --locked fails
fail
rollback --yes
fail: exit 1, --locked fails
fail
vendor --revert after uv remove python-dateutil + uv add six==1.16.0
PEP 723 script, uv add --script s.py six==1.16.0, then revert
fail-closed: drift-keep, both files untouched, still patched
same
recovery: uv lock, then vendor --revert
pass, converges, check 0
pass
The checks are text-level uuid checks with no OS-specific branch, so I didn't run a probe.
Suspect code
crates/socket-patch-core/src/vendor/pypi_uv.rs:795 (revert_uv): only the recorded records are reverted. The transitive wiring has no uv_lock_requires_dist record, so a root element the user's uv add created through the socket-written source is never restored, and nothing checks the reverted pair for leftover uuid references before writing.
crates/socket-patch-core/src/vendor/pypi.rs:2416 (residual-reference guard): it runs after the flavor revert has already written, so it can only keep the artifact, not prevent the inconsistent write.
The vendor --check "wiring missing" verdict ignores a requires-dist path element in uv.lock.
[agent] Triaged as p1 (uv). Possibly related to #1287 / #1337, which fix the vendored uv revert path for a transitive package whose lock fragment changed shape. This report covers a different transition (transitive → direct after uv add), and I haven't confirmed that #1337's change covers it, so this stays a separate issue for now.
[agent] Found by the scheduled uv bug-hunt routine (ledger #310).
Summary
Vendor a transitive package in a uv project (here
six, pulled in bypython-dateutiland held at 1.16.0 by a userconstraint-dependencies). Vendored mode wires it through[tool.uv] override-dependencies+[tool.uv.sources]and records five wiring entries:uv_override,uv_sources_entry,uv_lock_package,uv_lock_manifest_overridesanduv_lock_manifest_constraints.Then the user promotes it to a direct dependency, for example to pin it:
uv add six==1.16.0. uv writes a new rootrequires-distelement,{ name = "six", path = ".socket/vendor/pypi/<uuid>/six-1.16.0-…whl" }, because the socket-written source routes it. socket-patch never recorded that element.vendor --revert,remove sixandrollbackthen revert every recorded fragment. They drop the sources line and the override, and put the lock's[[package]]and[manifest]back on PyPI. They leave the rootrequires-distelement still pointing at the wheel. Only after writing does the residual-reference guard notice thatuv.lockstill names the uuid dir, and it keeps the wheel and the ledger entry.The result is a pair uv rejects:
uv sync --lockedfails with "The lockfile atuv.lockneeds to be updated".vendor --revertexits 0.removeandrollbackexit 1.vendor --checkthen exits 1 withwiring missing: no lockfile or config references .socket/vendor/pypi/<uuid> any more … re-run socket-patch vendor to rewire it. That's false, becauseuv.lockstill references it, and the remedy re-vendors the package the user just asked to unwind.Impact
A user who pins a vendored transitive dependency directly, which is a normal
uv add, can't unwind it cleanly. Every--locked/--frozen-checked CI install breaks after the revert. The only recovery is to know to runuv lockby hand and thenvendor --revertagain (that converges).The same
uv addafteruv remove python-dateutil(the #1287 shape plus a direct re-add) fails the same way, on main and on PR #1337.Repro
Requires a patch API serving a
six@1.16.0patch. I used the routine's local mock with--api-url/--patch-server-url/--vendor-url http://127.0.0.1:8765, abbreviatedspbelow.After the revert,
pyproject.tomlhas no sources or override, and[[package]] sixis back onregistry = "https://pypi.org/simple". The root[package.metadata] requires-diststill carries the path element.Expected vs actual
uv sync --lockedaccepts. Either restore the root element to the specifierpyproject.tomldeclares now ({ name = "six", specifier = "==1.16.0" }, the Vendored uv revert writes the pre-vendor specifier back into uv.lock after the user changes the vendored package's version spec, souv sync --lockedfails (vendor --revert / remove / rollback exit 0) #840 respell rule for root requirement entries), or, failing that, write nothing and drift-keep both files (fail closed, as the PEP 723 script lane already does for this exact shape). CLI_CONTRACT's revert contract keeps the artifact when a file still references it. It doesn't allow a half-written uv pair, and Vendored uv with a user-authoredoverride-dependencies: after any relock (uv add,uv lock --upgrade-package),vendor --revert/removerevert pyproject.toml but keep the vendored[manifest] overridesentry in uv.lock, souv sync --lockedfails (vendor --revert exits 0) #806 / Vendored uv package in a dependency group: afteruv add --dev/uv remove --dev,vendor --revert,remove,rollbackand the hosted takeover revert pyproject.toml but keep uv.lock's vendored requires-dev entry, souv sync --lockedfails (exit 0, "success") #821 / Vendored uv revert writes the pre-vendor specifier back into uv.lock after the user changes the vendored package's version spec, souv sync --lockedfails (vendor --revert / remove / rollback exit 0) #840 were the same class of half-revert.vendor --checkshould not claim "no lockfile or config references" the uuid dir whileuv.lockdoes.requires-distpath element is left,uv sync --lockedfails, andvendor --revertexits 0.Matrix (Linux, main
85105c9)vendor --revert(parent kept,uv add six==1.16.0)--lockedfails, check 1remove six --yes--lockedfailsrollback --yes--lockedfailsvendor --revertafteruv remove python-dateutil+uv add six==1.16.0scan --mode hosted)redirect_vendored_revert_failed, files untoucheduv add --script s.py six==1.16.0, then revertuv lock, thenvendor --revertThe checks are text-level uuid checks with no OS-specific branch, so I didn't run a probe.
Suspect code
crates/socket-patch-core/src/vendor/pypi_uv.rs:795(revert_uv): only the recorded records are reverted. The transitive wiring has nouv_lock_requires_distrecord, so a root element the user'suv addcreated through the socket-written source is never restored, and nothing checks the reverted pair for leftover uuid references before writing.crates/socket-patch-core/src/vendor/pypi.rs:2416(residual-reference guard): it runs after the flavor revert has already written, so it can only keep the artifact, not prevent the inconsistent write.vendor --check"wiring missing" verdict ignores arequires-distpath element in uv.lock.