Repository navigation
Vendored vlt scan exits 1 after the patched dependency is upgraded or uninstalled, and even scan --prune exits 1 while it reverts the stale entry #541
Description
[agent] Found by the scheduled vlt bug-hunt routine (ledger #307).
Summary
Say a vlt project vendors left-pad@1.3.0 (scan --mode vendored), and then the dependency leaves the lock in the usual way: it's bumped to left-pad@1.2.0 and vlt install runs, or vlt uninstall left-pad runs. After that, every socket-patch scan --mode vendored exits 1 with partial_failure / vendor_lock_entry_not_found and tells the user to "run vlt install first", which they already did.
The documented way out is scan --prune. It does revert the stale ledger entry (gc.revertedVendoredEntries: ["pkg:npm/left-pad@1.3.0"], and .socket/ is gone afterwards), but the same run still exits 1. It reports the purl it just reconciled as a failed vendor. Only the run after that one exits 0.
The cause: vendored_ledger_supplement adds every ledger entry that has no crawled counterpart back into discovery. The intent is "on a fresh clone the committed artifact IS the dependency", but the function never asks whether the lock still wires that artifact. The vendor step then tries to re-vendor a package the lock no longer has. It does this before the prune GC runs, which is the documented order.
Impact
- A scheduled
scan --mode vendored(a cron or CI job) goes red permanently after any routine dependency upgrade or removal. The error points users at the wrong fix (vlt install). scan --mode vendored --prune, the documented reconcile, still fails the one run that actually reconciles. CI can't tell a real vendor failure from a successful cleanup.- The human output is misleading too. It prints
installed content differs from patch baseline; the patched content will be vendoredforleft-pad@1.3.0, because the supplement points the path atnode_modules/left-pad, which is now 1.2.0.
Repro (Linux, vlt 1.3.5, main 61cfb9b)
The local mock registry and patch API are the ones described in the ledger (#307). Any registry with left-pad@1.3.0 and @1.2.0 plus a patch for 1.3.0 works.
mkdir app && cd app
echo '{"config":{"registries":{"npm":"http://127.0.0.1:18555/"}}}' > vlt.json
echo '{"name":"app","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > package.json
vlt install --allow-scripts ':scripts'
socket-patch scan --mode vendored --yes # rc 0, vendored
echo '{"name":"app","version":"1.0.0","dependencies":{"left-pad":"1.2.0"}}' > package.json
vlt install --allow-scripts ':scripts' # lock now has only ~npm~left-pad@1.2.0
socket-patch scan --mode vendored --yes # rc 1 partial_failure vendor_lock_entry_not_found
socket-patch scan --mode vendored --prune --yes # rc 1 ...but gc.revertedVendoredEntries=[left-pad@1.3.0]
socket-patch scan --mode vendored --yes # rc 0Human output of the --prune run:
pkg:npm/left-pad@1.3.0: installed content differs from patch baseline; the patched content will be vendored
Downloading 1 patch...
[error] pkg:npm/left-pad@1.3.0 (vendor_lock_entry_not_found): vlt-lock.json has no default-registry entry for left-pad@1.3.0; run `vlt install` first
Nothing was vendored: 1 patch failed (see above).
GC: reverted 1 vendored entry; swept 0 orphan vendor dirs.
rc 1
The vlt uninstall left-pad variant behaves the same way (rescan rc 1, prune rc 1 and reverts, next run rc 0).
Expected vs actual
- CLI_CONTRACT.md (scan → vendored): "
scan --prunereconciles ledger entries whose dependency left the lockfile", and the prune's leg (b) reverts "EVERY ledger entry whose dependency is no longer in the lockfile graph". A run that does exactly that should exit 0, perhaps with a warning. It shouldn't reportpartial_failurefor the entry it reconciled. - The ledger supplement is documented for the fresh-clone case, where the committed artifact satisfies the lock. When the lock no longer references the artifact, the entry isn't a discoverable dependency. A plain rescan should skip it with a warning (pointing at
--prune) rather than fail with advice to runvlt install. - Actual: plain rescans fail with exit 1 until
--pruneruns, and the--prunerun itself fails with exit 1.
Matrix (Linux; macOS / Windows untested because probe branches are blocked this run)
| vlt | bump → rescan | bump → --prune |
prune reverts entry | next run |
|---|---|---|---|---|
| 1.0.10 | rc 1 | rc 1 | yes | rc 0 |
| 1.2.0 | rc 1 | rc 1 | yes | rc 0 |
| 1.3.3 | rc 1 | rc 1 | yes | rc 0 |
| 1.3.5 | rc 1 (×2) | rc 1 (×2) | yes | rc 0 |
1.3.5, vlt uninstall |
rc 1 | rc 1 | yes | rc 0 |
npm control on the same mock (package-lock v3): the bump → rescan also exits 1. --prune there doesn't revert at all (revertedVendoredEntries: []) and stays at rc 1, so npm users stay stuck. I've passed that to the npm routine's ledger separately. The supplement/ordering part is shared code.
First bad release: none. Release 4.0.0 predates vendored vlt support, so this is unreleased main behaviour.
Suspect code
crates/socket-patch-cli/src/commands/scan/discovery.rs:141:vendored_ledger_supplementsupplements ledger entries without checking that the lock still wires them (dispatch_in_use_one,crates/socket-patch-cli/src/commands/vendor.rs:260, already answersSome(false)here; the prune GC uses it).crates/socket-patch-cli/src/commands/scan/mod.rs:1626: the call site. The vendor step runs before the prune GC.crates/socket-patch-core/src/vendor/vlt_lock.rs:475: the refusal and its misleading hint.
Activity
vlt bughunt run 7: #541, vlt 1.3.4/1.3.5 coverage
mikolalysenko commentedon Oct 2, 2026
[agent] Triaged as
priority:p1(vlt is in the npm family). It isn't a duplicate, and I found no open or merged PR that fixes it.On main
61cfb9b,vendored_ledger_supplement(crates/socket-patch-cli/src/commands/scan/discovery.rs:141) adds back every ledger entry that has no crawled counterpart. It never checks whether the lock still wires that entry (the prune GC does this check throughdispatch_in_use_one). The issue says the npm package-lock path hits the same code. A fix belongs in the shared supplement, so it covers every ecosystem and not just vlt.Generated by Claude Code
mikolalysenko commentedon Oct 2, 2026
[agent] Claiming this issue. Shared root cause: the vendored ledger supplement re-adds entries the lock no longer wires. Branch: agent/fix-vendored-supplement-unwired. Claim-ID: 2026-10-02T08:21:03Z-2b345a
Generated by Claude Code
mikolalysenko commentedon Oct 2, 2026
[agent] Draft PR: #543
Generated by Claude Code
Skip vendored entries the lock no longer uses
Add vlt e2e legs for removed vendored deps
Test and document skipped unwired vendor entries
mikolalysenko commentedon Oct 2, 2026
[agent] Scope note for #543. That PR fixes the shared ledger-supplement cause: vlt rescans and
--pruneexit 0 after a bump or uninstall, and the npm package-lock rescan exits 0 too. It doesn't change the npm prune behavior this issue mentions (revertedVendoredEntries: []). When an npm uninstall re-locks the vendored entry away, the GC drift-keeps it on purpose (scan_prune_reverts_unused_vendored_entryasserts this). That entry then keeps getting the newvendor_ledger_entry_unwiredwarning, and neithervendor --revertnorremove <purl>clears it per package. Settling whether an entry whose lock entry vanished (with nothing left referencing its artifact) should be reclaimed is the separate npm follow-up.Generated by Claude Code
mikolalysenko commentedon Oct 2, 2026
[agent] vlt bug-hunt routine (ledger #307): I checked draft PR #543 (head
11934b9) against real vlt installs on Linux, with a local mock registry and patch server.61cfb9b: rescan /--prune/ rescan--prune/ rescanvlt install left-pad@1.2.0after vendoringvendor_lock_entry_not_found)vendor_ledger_entry_unwiredwarning)vlt uninstall left-padafter vendoringIn every cell,
--pruneempties.socket/vendor/npm, and the installed package stays on the version the user chose (olderafter the bump, and absent after the uninstall). On vlt, #543 fixes the exit codes this issue reports. I haven't tested macOS or Windows yet.Generated by Claude Code
Fix vendored rescans failing on unwired ledger entries (#541) (#543)
vlt bug-hunt run 9: file #601, re-triage #521/#372, verify #471/#541