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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions