Hacktoberfest 2026: as issues que os mantenedores marcaram para outubro, abertas e boas para iniciantes. Ver issues do Hacktoberfest

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

Fechada
#1,374 3 comentários 0 reações 0 responsáveis Ver no GitHub

Mantenedores costumam responder em até 1 dia

@mikolalysenko já está trabalhando nisso.

Desde 11/10/2026.

  • #1384 de @mikolalysenko — aberto

Avaliação

Dificuldade
4/5
Tempo estimado
3-5 dias
Facilidade para iniciantes
28/100
Tipo de issue
Bug
Clareza
Razoavelmente clara
Status de atividade
Ativa
Stack de tecnologia
python, rust
Domínio
build-system, cli

Direção de pesquisa

Start with revert_uv in crates/socket-patch-core/src/vendor/pypi_uv.rs (around line 795), which reverts only the recorded wiring fragments, then the residual-reference guard in crates/socket-patch-core/src/vendor/pypi.rs (around line 2416). Reproduce with the repro steps in the issue, then check that uv sync --locked passes and vendor --check agrees with uv.lock after vendor --revert. Done means the root requires-dist element is restored or the revert fails closed, and the check no longer reports a false 'wiring missing'.

Escrita pelo modelo de indexação a partir do texto da issue.

Descrição

agent:claimed agent:triaged bug bughunt pm:uv priority:p1

[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 [email protected] 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.

Expected vs actual

  • Expected: the revert leaves a pair uv sync --locked accepts. Either restore the root element to the specifier pyproject.toml declares now ({ name = "six", specifier = "==1.16.0" }, the #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 #806 / #821 / #840 were the same class of half-revert.
  • 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 fail (main and PR #1337) fail (main and PR #1337)
hosted takeover (scan --mode hosted) pass: redirect_vendored_revert_failed, files untouched pass
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.
Linguagem predominante
Rust
Estrelas
8
Forks
1
Merge médio
19h 1min
PRs com merge (30d)
473

Preparar o ambiente

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Mais de SocketDev/socket-patch

Todas as issues de SocketDev/socket-patch

Issues semelhantes

Mais issues de Rust

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.