[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.
[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. Bothget <uuidB> --mode vendoredandscan --mode vendored(once the API offers only B) download B, report it as superseding A (oldUuid= A), and then the vendor step refuses:The run exits 1 (
partial_failure). requirements.txt,.socket/vendor/pypi/<A>/and the ledger all stay on A, sopip install -r requirements.txtkeeps installing patch A's bytes. The--dry-runof the same command previewswould_revendorwitholdUuid: Aand exits 0.The uv routine first reported this on the
scan --mode vendoredpath (handover in ledger #309; the uv-specific half is #742). This issue confirms it with real pip andget --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 --revertand vendors again by hand. Other vendored backends re-vendor in place (npm:human_vendored_uuid_supersede_prints_replacing_and_vendors; cargo / gem / pnpm haverevendor_new_uuidtests).Expected vs actual
CLI_CONTRACT.md, the
scan --vendorparagraph: "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 thedownloadedrecord carryingoldUuid, "the re-vendor the vendor step then performs"../.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-runpreviews.pypi_requirements_already_vendored, nothing changes.Repro (main
045d7ec, Linux, real pip)The mock is the repo's
prebuilt_commonfixture (mount_view_from_sourceover the real installedsix.py, plus aGET /v0/orgs/acme/patches/view/<uuid>route per uuid). The two records patchsix.pydifferently, appendingSOCKET_PATCHED = 'A'or'B'.Workaround:
socket-patch vendor --revert(restoressix==1.16.0), then vendor B again.Matrix
six==1.16.0+idna==3.7, unhashedsix==1.16.0 \+--hash=sha256:…, hasheduv pip sync) / 3.11,scan --mode vendoredFirst 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 returnsErr("pypi_requirements_already_vendored")(line 159) instead of a re-wire plan, and the unit test atcrates/socket-patch-core/src/vendor/pypi.rs:2707pins 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.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.