Repository navigation
Human scan --mode vendored --prune silently skips the vendored GC when no remaining package has a patch, so an npm uninstalled vendored entry is never reverted (exit 0), while --json reverts it and vendor --check keeps pointing at that same command #1127
Description
Activity
- addedbugSomething isn't workingSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentFound by a scheduled package-manager bug-hunt agentpm:npmnpmnpm
on Oct 8, 2026 - added a commit that references this issue
on Oct 8, 2026 mikolalysenko commented
on Oct 8, 2026 CollaboratorAuthorMore actions[agent] Triage: priority:p1 (npm-family vendored). I confirmed this in the code on main
e2d9633. In the human path,finish_human(crates/socket-patch-cli/src/commands/scan/mod.rs:2673) runsgc::run_human_gconly whenprune && !vendor && !hosted. The earlyall_packages_with_patches.is_empty()return at :2688 goes throughfinish_humanbefore the vendored arm, so no GC runs for--mode vendored --prune. The JSON path callsgc_jsonunconditionally (:2632), which is why--jsonreverts the entry. I found no open or merged PR for this, and it isn't a duplicate. It also doesn't share a cause with #1063: that one reads a corrupt manifest as empty, which is a different code path.
Generated by Claude Code
- addedv5-blockerMust resolve before v5: public interface/migration or ordinary patch-install-undo failure.Must resolve before v5: public interface/migration or ordinary patch-install-undo failure.uxCLI commands, help, diagnostics, output consistency, or actionable recovery instructions.CLI commands, help, diagnostics, output consistency, or actionable recovery instructions.compatibilityPublic CLI/JSON, saved state, upgrades, or package-manager compatibility.Public CLI/JSON, saved state, upgrades, or package-manager compatibility.and removed
on Oct 8, 2026 mikolalysenko commented
on Oct 9, 2026 CollaboratorAuthorMore actionsv5 release blocker (P1). The suggested prune command must perform the same cleanup in human and JSON mode after an ordinary dependency removal.
This follows the maintainer's release scope: one normally completing CLI instance, prioritizing valid-lockfile patch/install behavior, compatibility, and actionable CLI UX.
mikolalysenko commented
on Oct 9, 2026 CollaboratorAuthorMore actions[agent] Pipenv bug-hunt routine (ledger #313): this isn't limited to the npm family. It reproduces the same way for vendored PyPI / Pipenv on main
2e0d17f(Linux, real Pipenv 2023.12.1 and 2026.8.0 on py3.11, local mock patch API serving a patchedsix 1.16.0wheel). Reproduced 2/2.# Pipfile: [packages] six==1.16.0, [dev-packages] idna==3.7 (or six alone in a named [tests] category beside certifi) pipenv lock socket-patch scan --mode vendored --yes # wires six -> ./.socket/vendor/pypi/<uuid>/six-…whl git add -A && git commit -qm vendored pipenv uninstall six # (or: pipenv uninstall six --categories tests) socket-patch vendor --check; echo $? # 1: "dependency removed … run `socket-patch scan --mode vendored --prune`" socket-patch scan --mode vendored --yes --prune # exit 0, "No patches available for installed packages." No GC line socket-patch vendor --check; echo $? # still 1; .socket/vendor/pypi/<uuid>/ and the ledger entry stay socket-patch scan --mode vendored --yes --prune --json | jq .gc.revertedVendoredEntries # ["pkg:pypi/six@1.16.0"] socket-patch vendor --check; echo $? # 0
Pipenv category human --prune--json --prune2023.12.1 [packages]no GC, check stays 1 reverted, check 0 2023.12.1 named [tests](other packages left)no GC, check stays 1 reverted, check 0 2026.8.0 [packages]no GC, check stays 1 not run (same code path) The cause is the one in the triage note above.
finish_human(nowcrates/socket-patch-cli/src/commands/scan/mod.rs:2802) skips the GC whenvendoris set, and the empty-discovery return at:2825exits before the vendored flow's own GC. So any ecosystem whose last vendored dependency was removed (while other packages remain, so the crawl isn't empty) takes this path.
Generated by Claude Code
mikolalysenko commented
on Oct 9, 2026 CollaboratorAuthorMore actions[agent] Claiming for v5 blocker burn-down (shared root cause: scan's human early return for 'no patches' runs finish_human without the vendored --prune GC). Branch: agent/v5-human-prune-vendored-gc. Claim-ID: 2026-10-09T16:41:47Z-588531
- added 2 commits that reference this issue
on Oct 9, 2026 - added a commit that references this issue
on Oct 10, 2026
[agent] Found by the scheduled npm bug-hunt routine (ledger #302).
Summary
You vendor a patched npm dependency, then remove it with
npm uninstall.vendor --checkthen exits 1 withdependency removed … run socket-patch scan --mode vendored --prune to revert the vendored entry. When none of the project's remaining packages has a patch, running that command in human mode printsNo patches available for installed packages., exits 0, and does nothing. The ledger entry and.socket/vendor/npm/<uuid>/stay, andvendor --checkstill exits 1. The same command with--jsondoes revert the entry (gc.revertedVendoredEntries: ["pkg:npm/left-pad@1.3.0"]).Impact
vendor --checksends you to it, it exits 0, andvendor --checkstill fails. CI that gates onvendor --checkstays red until someone uses--json(by accident) orvendor --revert.--pruneis set, thevendor_ledger_entry_unwiredwarning is suppressed (prune_reverts_unwired, scan/mod.rs:1843). The human run gives no hint that anything was left behind.--jsonoutput disagree about what the same command writes.Repro (npm 10.9.4 / Node 22, also npm 12.2.0 / Node 24; local mock of the public patch proxy serving a free patch for
left-pad@1.3.0)Control: when a remaining package does have a patch (the mock also serves
ms@2.1.3), the human run printsGC: reverted 1 vendored entryandvendor --checkgoes green. When no packages are left at all, the zero-package path runsgc::run_vendor_only_gc, so that case works too. Only "packages found, none patched" is broken.Expected vs actual
--prunereports it through the run-levelvendor_ledger_entry_unwiredwarning; a--prunerun reverts it in its GC and exits 0. That GC runs even when the crawl found no packages". Also thescan --pruneparagraph: "(b) EVERY ledger entry whose dependency is no longer in the lockfile graph is reverted".--json: GC runs and reverts the entry.Matrix (Linux)
--prunereverts--json --prunerevertsnpm uninstall -w a)This is not OS-specific: it comes from scan control flow, not from the filesystem, so I didn't push a probe branch. It's not npm-specific either: any vendored ecosystem whose remaining packages have no patches should hit it. v4.0.0 can't vendor against this mock (a different vendoring flow), so there's no release bisect. main's history is grafted at
23fd62e, so the first bad commit can't be bisected.Suspect code (main
e2d9633)crates/socket-patch-cli/src/commands/scan/mod.rs:2688:if all_packages_with_patches.is_empty() { … return finish_human(0).await; }crates/socket-patch-cli/src/commands/scan/mod.rs:2673:finish_humanruns the GC onlyif prune && !vendor && !hosted, because the vendored arm "runs its own". On this early return the vendored arm never runs, so nothing does.gc_json(scan/mod.rs:2633) regardless, which is why--jsonworks.scan/mod.rs:1843-1844:prune_reverts_unwiredsuppresses thevendor_ledger_entry_unwiredwarning on the assumption that the GC will run.Backlog review — 2026-10-08
Priority: P1 → P2. The human scan early return skips vendored GC, leaving an obsolete entry and a failing vendor --check. This is a real cleanup/control-flow bug; --json or vendor --revert provides a workaround. The report establishes neither lost recovery data nor a false security attestation, so P2 is appropriate.