Repository navigation
After bun remove of a vendored package in a bun.lockb project, scan --prune, vendor --revert and remove keep it as "drifted", so vendor --check stays red and its remedy loops #1132
Description
[agent] Found by the scheduled Bun bug-hunt routine (ledger #306).
Summary
In a vendored project that uses a binary bun.lockb, run bun remove <pkg> on a vendored package. No socket-patch command can then clean up its vendored entry:
vendor --checkexits 1 withdependency removed: no lockfile resolves pkg:npm/minimist@1.2.2 any more … run socket-patch scan --mode vendored --prune to revert the vendored entry(the Fix vendor --check unwired cause and remedy (#900) #970 / Decide vendored-entry liveness through one discovery verdict #1050 message).scan --mode vendored --pruneexits 0 but keeps the entry:GC: kept 1 drifted vendored entry: lock entries were re-resolved since vendoring … undo the drift and re-run vendor --revert to finish(keptVendoredEntries: ["pkg:npm/minimist@1.2.2"]).vendor --revertexits 0 withKept 1 drifted package(vendor_lock_entry_drifted: binary package resolution has drifted).remove pkg:npm/minimist@1.2.2exits 1 with1 matching entry was drift-kept …; re-run scan --mode vendored to normalize, then remove again. That re-run changes nothing.
The .socket/vendor/npm/<uuid>/ tarball and its ledger entry stay committed, and vendor --check stays red in CI for good. Only hand-deleting the uuid dir and editing .socket/vendor/state.json gets you out.
On a text bun.lock, and on npm's package-lock.json, the same removal is reverted cleanly: prune prints GC: reverted 1 vendored entry and vendor --check goes back to exit 0. So the defect is specific to the binary lockb backend.
Expected (CLI_CONTRACT.md, vendor --revert, lines 803–807)
in the npm family (npm, yarn classic and berry, pnpm, bun) a recorded lock entry that no longer exists at all — the user removed the dependency — is not drift: it warns
vendor_lock_entry_removed… sorollback/remove/scan --pruneclean up afternpm uninstall/yarn remove/pnpm remove/bun remove
The scan --prune paragraph (line 137, leg (b)) also says: "EVERY ledger entry whose dependency is no longer in the lockfile graph is reverted".
Actual
crates/socket-patch-core/src/vendor/bun_binary.rs:520-527 (revert): when the lock contains neither the rewritten snapshot nor the original for the recorded package ID, the record is reported as vendor_lock_entry_drifted ("binary package resolution has drifted"). There's no "entry no longer exists → vendor_lock_entry_removed, nothing to restore" arm like the one the text backend has (bun_lock.rs, the test around line 3600) and npm has (npm_lock.rs ~3350). outcome.drift_skipped() then keeps the artifact, so run_vendor_gc (commands/vendor.rs ~4290) lists it under kept. Meanwhile Discovery::vendor_entry_in_use correctly returns Some(false), which is why vendor --check sends you to --prune.
Repro (Linux, real Bun, local mock of the patch API with a minimist@1.2.2 patch)
# usage: repro.sh <bun-binary>
BUN=$1; D=$(mktemp -d); cd "$D"; git init -q; echo node_modules/ > .gitignore
printf '[install]\nsaveTextLockfile = false\nlinker = "hoisted"\n' > bunfig.toml
echo '{"name":"proj","version":"1.0.0","dependencies":{"minimist":"1.2.2","left-pad":"1.3.0"}}' > package.json
"$BUN" install && git add -A && git commit -qm init
socket-patch scan --mode vendored # exit 0, both packages vendored into bun.lockb
"$BUN" install --frozen-lockfile && git add -A && git commit -qm vendored
"$BUN" remove minimist && git add -A && git commit -qm "drop minimist"
socket-patch vendor --check; echo $? # 1: "dependency removed … run scan --mode vendored --prune"
socket-patch scan --mode vendored --prune # exit 0: "GC: kept 1 drifted vendored entry …"
socket-patch vendor --check; echo $? # still 1
socket-patch vendor --revert # exit 0: "Kept 1 drifted package"
socket-patch remove pkg:npm/minimist@1.2.2; echo $? # 1: "drift-kept … re-run scan … then remove again"
ls .socket/vendor/npm/ # 11111111-… (minimist) still there(left-pad keeps a patched package in the project, so the scan's GC runs. See #1127 for the case where no patched package remains.)
Matrix (main 9472be4, Linux)
| Bun | lock | shape | bun remove → prune / revert / remove |
vendor --check afterwards |
|---|---|---|---|---|
| 1.1.39 | bun.lockb |
single | kept as drifted | exit 1 |
| 1.2.23 | bun.lockb |
single, workspace member | kept as drifted | exit 1 |
| 1.3.9 | bun.lockb |
single (hoisted + isolated), workspace | kept as drifted | exit 1 |
| 1.4.2 | bun.lockb |
single (hoisted + isolated), workspace (hoisted + isolated) | kept as drifted | exit 1 |
| 1.2.23 / 1.3.9 / 1.4.2 | text bun.lock |
single, workspace (1.4.2) | reverted | exit 0 |
| npm 10 | package-lock.json |
single | reverted | exit 0 |
It reproduced in two separate runs of each lockb cell. macOS and Windows weren't tested: the backend is platform-independent, and no probe branch was available this run.
Related, but not Bun-specific, so it's being handed to the npm routine rather than filed here: when the dependency is upgraded off the patched version (bun add minimist@1.2.8, npm install minimist@1.2.8), every backend (text bun.lock on 1.1.39–1.4.2, bun.lockb, package-lock.json) gives the same loop. vendor --check says "dependency removed … run --prune", and prune keeps the entry as "drifted".
First bad: not bisected. Release 4.0.0 can't vendor against the local mock. The --prune remedy text came with #970 and the shared in-use verdict with #1050, but the lockb revert arm was already there before them.
Suspect code: crates/socket-patch-core/src/vendor/bun_binary.rs:526.
Activity
bun: run 32 ledger (#1132, #599 comment, npm handover)
mikolalysenko commentedon Oct 8, 2026
[agent] Triaged as p1 (Bun). Confirmed on
mainby reading the code:vendor::bun_binary::revert(crates/socket-patch-core/src/vendor/bun_binary.rs, thefind_snapshot_idmatch) maps "neither the rewritten nor the original snapshot is in the lock" straight tovendor_lock_entry_drifted. There is no "package no longer resolved →vendor_lock_entry_removed, nothing to restore" arm like the textbun.lockand npm backends have, sooutcome.drift_skipped()keeps the artifact and every cleanup path loops. No shared root cause with other open issues found; no duplicate or open PR covers it.Generated by Claude Code
mikolalysenko commentedon Oct 8, 2026
[agent] Claiming this issue (with #1140, #1142; shared root cause: the bun.lockb, uv and Pipenv vendored revert backends map a lock entry the package manager removed to
vendor_lock_entry_driftedinstead of the sharedvendor_lock_entry_removedarm the npm-family text backends use, so every unwind keeps the entry). Branch: agent/fix-vendored-removed-dep-drift. Claim-ID: 2026-10-08T16:20:32Z-8b1b90Generated by Claude Code
mikolalysenko commentedon Oct 8, 2026
[agent] Draft PR: #1147
Generated by Claude Code
Let bun.lockb revert a package bun remove dropped
Let bun.lockb revert a package bun remove dropped
Test bun remove then vendor --revert on bun.lockb
Merge origin/main into agent/fix-bun-open-issues