Skip to content

Vendored requirements.txt never picks up a superseding patch: the re-vendor to a new uuid fails with pypi_requirements_already_vendored (exit 1), though --dry-run previews would_revendor and the contract says it re-vendors automatically #765

Description

[agent] Found by the scheduled pip / requirements.txt bug-hunt routine (ledger #309).

Summary

A pip project vendored at patch uuid A never moves to a newer patch B for the same pkg:pypi/six@1.16.0. Both get <uuidB> --mode vendored and scan --mode vendored (once the API offers only B) download B, report it as superseding A (oldUuid = A), and then the vendor step refuses:

failed  pypi_requirements_already_vendored
requirements.txt: already routes six to the socket-patch vendored wheel for patch <A>;
run `socket-patch vendor --revert` before re-vendoring

The run exits 1 (partial_failure). requirements.txt, .socket/vendor/pypi/<A>/ and the ledger all stay on A, so pip install -r requirements.txt keeps installing patch A's bytes. The --dry-run of the same command previews would_revendor with oldUuid: A and exits 0.

The uv routine first reported this on the scan --mode vendored path (handover in ledger #309; the uv-specific half is #742). This issue confirms it with real pip and get --mode vendored.

Impact

Vendored pip users can't receive an updated patch, such as a fix to a patch or a patch covering more CVEs. The CI or bot run that should roll them forward fails instead, and the committed wheel stays on the old patch until someone runs vendor --revert and vendors again by hand. Other vendored backends re-vendor in place (npm: human_vendored_uuid_supersede_prints_replacing_and_vendors; cargo / gem / pnpm have revendor_new_uuid tests).

Expected vs actual

CLI_CONTRACT.md, the scan --vendor paragraph: "A package the ledger holds at an older patch uuid is still re-vendored automatically when discovery selects the newer patch (its old uuid dir is removed — vendor_stale_artifact_removed)". The same paragraph also covers the downloaded record carrying oldUuid, "the re-vendor the vendor step then performs".

  • Expected: the requirements.txt line is rewired to ./.socket/vendor/pypi/<B>/six-1.16.0-….whl, <A>/ is removed (vendor_stale_artifact_removed), the ledger moves to B, and the run exits 0. That's what --dry-run previews.
  • Actual: exit 1, pypi_requirements_already_vendored, nothing changes.

Repro (main 045d7ec, Linux, real pip)

The mock is the repo's prebuilt_common fixture (mount_view_from_source over the real installed six.py, plus a GET /v0/orgs/acme/patches/view/<uuid> route per uuid). The two records patch six.py differently, appending SOCKET_PATCHED = 'A' or 'B'.

printf 'six==1.16.0\nidna==3.7\n' > requirements.txt
python3.11 -m venv .venv && .venv/bin/pip install -r requirements.txt
F="--api-url $M --api-token fake --org acme --yes --vendor-url $M"
socket-patch get $UUID_A --mode vendored --json $F   # exit 0, line -> ./.socket/vendor/pypi/$UUID_A/six-1.16.0-py2.py3-none-any.whl
socket-patch get $UUID_B --mode vendored --json --dry-run $F
#   exit 0, vendor.patches: [{action: "would_revendor", uuid: B, oldUuid: A}]
socket-patch get $UUID_B --mode vendored --json $F
#   exit 1, status partial_failure, patches[0]: {action: downloaded, oldUuid: A}
#   vendor.events: [{action: failed, errorCode: pypi_requirements_already_vendored}]
cat requirements.txt                                   # still ./.socket/vendor/pypi/$UUID_A/…whl
python3.11 -m venv v2 && v2/bin/pip install -r requirements.txt
v2/bin/python -c "import six; print(six.SOCKET_PATCHED)"   # A

Workaround: socket-patch vendor --revert (restores six==1.16.0), then vendor B again.

Matrix

OS pip / Python requirements shape result
Linux 24.0 / 3.11 six==1.16.0 + idna==3.7, unhashed fail
Linux 20.3.4 / 3.11 six==1.16.0 \ + --hash=sha256:…, hashed fail
Linux uv 0.8.17 (uv pip sync) / 3.11, scan --mode vendored unhashed fail (uv routine)
macOS / Windows — — untested; the logic is a text pre-flight and is OS-independent

First bad release: not a regression. v4.0.0 (PyPI socket-patch==4.0.0) fails the same way with the same error code.

Suspect code

  • crates/socket-patch-core/src/vendor/pypi_requirements.rs:144, preflight_requirements. A vendor line for the package at a different uuid returns Err("pypi_requirements_already_vendored") (line 159) instead of a re-wire plan, and the unit test at crates/socket-patch-core/src/vendor/pypi.rs:2707 pins that refusal. The CLI's re-vendor path (sweep_stale_artifact, crates/socket-patch-cli/src/commands/vendor.rs:1675) and the dry-run preview both assume the backend re-wires in place.
  • The dry-run preview (would_revendor) doesn't run this pre-flight, so it diverges from the wet run.

No probe runs: the failing check is a pure text pre-flight, so it's OS-independent.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedbugSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentpm:pippip / requirements.txtpriority:p1

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions