[agent] Found by the scheduled Poetry bug-hunt routine (ledger #311).
Summary
After socket-patch vendor on a Poetry project, poetry.lock points at .socket/vendor/pypi/<uuid>/<wheel> and its files = [...] hash is the patched wheel's sha256. If the vendored wheel is then missing on a checkout with no installed copy (a fresh clone with no venv, CI, or someone deleted the wheel), socket-patch repair fails:
vendor_artifact_unrepairable: no PyPI release file for [email protected] matches the lockfile's sha256 730669e84c15…
socket-patch vendor re-run in the same state skips with vendor_fetch_unverifiable and package_not_installed.
The vendor ledger (.socket/vendor/state.json, wiring[].original) still holds the pristine lock fragment with the real PyPI hash sha256:8abb2f1d…. fetch_pristine_package was written to fall back to that fragment. It never does for Poetry, because the Poetry lock inventory reports the rewired entry as a fetchable registry resolution whose integrity is the patched hash.
uv on the same fixture rebuilds the wheel (rebuilt, exit 0). Pipenv's inventory handles this correctly: it maps Socket's own references to LockIntegrity::None, so they fall through to the ledger (lock_inventory/pypi.rs:478-495).
Impact
A vendored Poetry project can't self-heal a missing or corrupt vendored wheel unless the package also happens to be installed. poetry install then fails because the file source is gone. The error message also blames PyPI and the lockfile. CLI_CONTRACT.md reserves vendor_artifact_unrepairable for "not installed + lockfile rewired + no recoverable ledger fragment", but a recoverable fragment exists here.
Repro (Linux, Poetry 2.3.3; the synthetic manifest needs no API key)
mkdir demo && cd demo
cat > pyproject.toml <<'EOF'
[tool.poetry]
name = "demo"
version = "0.1.0"
description = ""
authors = ["x <x@x>"]
package-mode = false
[tool.poetry.dependencies]
python = "^3.8"
six = "1.16.0"
EOF
poetry lock
# .socket/manifest.json with one patch for pkg:pypi/[email protected] (six.py before/after git-sha256)
# plus .socket/blobs/<afterHash>. Any real six patch works.
socket-patch vendor --json # success; lock rewired to the vendored wheel
POETRY_VIRTUALENVS_IN_PROJECT=true poetry install --no-root # six.py is patched ✔
rm -rf .venv .socket/vendor/pypi/*/six-1.16.0-py2.py3-none-any.whl
socket-patch repair --json # ❌ failed / vendor_artifact_unrepairable
socket-patch vendor --json # ❌ skipped / vendor_fetch_unverifiable + package_not_installed
In the sandbox, SOCKET_PYPI_JSON_API pointed at a local HTTP forwarder for pypi.org, because rustls doesn't trust the sandbox proxy CA. With the same forwarder, the initial lock-only vendor fetch and the uv repair both succeed, so the fetch path works.
Expected vs actual
- Expected: repair recovers the pre-vendor registry resolution from the ledger's
wiring[].original fragment (vendor.rs doc for PristineFetch: "the ledger-recovered pre-vendor registry fragment … only --revert's restore data still knows the registry resolution"), fetches six-1.16.0-py2.py3-none-any.whl by sha256:8abb2f…, rebuilds, and reports rebuilt. CLI_CONTRACT.md only allows vendor_artifact_unrepairable when there is "no recoverable ledger fragment". uv does exactly this.
- Actual: repair queries PyPI for the patched hash, finds nothing, and fails with exit 1. A
vendor re-run can't rebuild either.
Matrix (Linux, main f6b7fb9)
| Poetry |
lock-version |
vendor + poetry install |
repair (lock-only, wheel deleted) |
vendor re-run (same state) |
| 1.1.15 |
1.1 |
✅ patched |
❌ unrepairable |
❌ fetch_unverifiable |
| 1.2.2 |
1.1 |
✅ patched |
❌ unrepairable |
❌ fetch_unverifiable |
| 1.8.5 |
2.0 |
✅ patched |
❌ unrepairable |
❌ fetch_unverifiable |
| 2.0.1 |
2.1 |
✅ patched |
❌ unrepairable |
❌ fetch_unverifiable |
| 2.3.3 |
2.1 |
✅ patched |
❌ unrepairable (reproduced 3×, across 2 runs) |
❌ fetch_unverifiable |
| 2.4.3 |
2.1 |
✅ patched |
❌ unrepairable |
❌ fetch_unverifiable |
| uv (contrast) |
uv.lock |
✅ |
✅ rebuilt |
n/a |
If an installed copy exists, repair rebuilds fine. macOS and Windows weren't probed. The defect is in platform-independent lock parsing.
First bad: not reproducible on release 4.0.0 or 3.3.0, which can't vendor a lock-only Poetry checkout at all (vendor_fetch_unverifiable at the first vendor). The bug comes with the unreleased lock-only Poetry fetch path (PyPI JSON API lookup by lock hash, SOCKET_PYPI_JSON_API, first added in #241).
Suspect code
crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs:346-372 (inventory_poetry_lock): it takes the -none-any.whl hash from files without checking [package.source] type = "file" / url = ".socket/vendor/pypi/…". So a Socket-rewired entry gets LockIntegrity::Sha256Hex(<patched hash>) instead of LockIntegrity::None (compare the Pipfile.lock branch at pypi.rs:478-495).
crates/socket-patch-cli/src/commands/vendor.rs:1193-1198 (fetch_pristine_package): the (Some(e), _) => e arm prefers that "fetchable" inventory entry, so recover_lock_entry on the ledger fragment is never reached.
[agent] Found by the scheduled Poetry bug-hunt routine (ledger #311).
Summary
After
socket-patch vendoron a Poetry project,poetry.lockpoints at.socket/vendor/pypi/<uuid>/<wheel>and itsfiles = [...]hash is the patched wheel's sha256. If the vendored wheel is then missing on a checkout with no installed copy (a fresh clone with no venv, CI, or someone deleted the wheel),socket-patch repairfails:socket-patch vendorre-run in the same state skips withvendor_fetch_unverifiableandpackage_not_installed.The vendor ledger (
.socket/vendor/state.json,wiring[].original) still holds the pristine lock fragment with the real PyPI hashsha256:8abb2f1d….fetch_pristine_packagewas written to fall back to that fragment. It never does for Poetry, because the Poetry lock inventory reports the rewired entry as a fetchable registry resolution whose integrity is the patched hash.uv on the same fixture rebuilds the wheel (
rebuilt, exit 0). Pipenv's inventory handles this correctly: it maps Socket's own references toLockIntegrity::None, so they fall through to the ledger (lock_inventory/pypi.rs:478-495).Impact
A vendored Poetry project can't self-heal a missing or corrupt vendored wheel unless the package also happens to be installed.
poetry installthen fails because thefilesource is gone. The error message also blames PyPI and the lockfile. CLI_CONTRACT.md reservesvendor_artifact_unrepairablefor "not installed + lockfile rewired + no recoverable ledger fragment", but a recoverable fragment exists here.Repro (Linux, Poetry 2.3.3; the synthetic manifest needs no API key)
In the sandbox,
SOCKET_PYPI_JSON_APIpointed at a local HTTP forwarder for pypi.org, because rustls doesn't trust the sandbox proxy CA. With the same forwarder, the initial lock-onlyvendorfetch and the uv repair both succeed, so the fetch path works.Expected vs actual
wiring[].originalfragment (vendor.rsdoc forPristineFetch: "the ledger-recovered pre-vendor registry fragment … only--revert's restore data still knows the registry resolution"), fetchessix-1.16.0-py2.py3-none-any.whlbysha256:8abb2f…, rebuilds, and reportsrebuilt. CLI_CONTRACT.md only allowsvendor_artifact_unrepairablewhen there is "no recoverable ledger fragment". uv does exactly this.vendorre-run can't rebuild either.Matrix (Linux, main
f6b7fb9)poetry installrebuiltIf an installed copy exists, repair rebuilds fine. macOS and Windows weren't probed. The defect is in platform-independent lock parsing.
First bad: not reproducible on release 4.0.0 or 3.3.0, which can't vendor a lock-only Poetry checkout at all (
vendor_fetch_unverifiableat the firstvendor). The bug comes with the unreleased lock-only Poetry fetch path (PyPI JSON API lookup by lock hash,SOCKET_PYPI_JSON_API, first added in #241).Suspect code
crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs:346-372(inventory_poetry_lock): it takes the-none-any.whlhash fromfileswithout checking[package.source] type = "file"/url = ".socket/vendor/pypi/…". So a Socket-rewired entry getsLockIntegrity::Sha256Hex(<patched hash>)instead ofLockIntegrity::None(compare the Pipfile.lock branch atpypi.rs:478-495).crates/socket-patch-cli/src/commands/vendor.rs:1193-1198(fetch_pristine_package): the(Some(e), _) => earm prefers that "fetchable" inventory entry, sorecover_lock_entryon the ledger fragment is never reached.