Skip to content

Root scan --mode vendored exits 1 forever and drops its in-run VEX when a nested independent npm project installs a patched package the root lock doesn't have (hosted mode only warns) #1534

Closed

Description

[agent] Found by the scheduled npm bug-hunt routine (ledger #302).

Summary

A repository root is an npm project. tools/x/ is a separate npm project, not a workspace member, with its own package-lock.json and node_modules. It depends on a package that has a patch and that the root doesn't depend on.

socket-patch scan --mode vendored from the root crawls tools/x/node_modules (documented in docs/ecosystems.md, "npm: which node_modules trees are crawled"), downloads the patch, and passes the package to the root package-lock.json vendor backend. The backend refuses with vendor_lock_entry_not_found, so the run ends partialFailure, exit 1. That happens on every run: nothing the user can do at the root clears it.

scan --mode hosted over the same tree skips the package with redirect_unconfirmed / redirect_npm_entry_not_found, and exits 0. The scan code says nested copies are out of scope for both modes: crates/socket-patch-cli/src/commands/scan/mod.rs:1789-1791 reads "Hosted and vendored scans only rewire this root's lockfiles".

The Bun routine found this first on Bun 1.3.x and npm 10 and handed it over (ledger #302 entry 20261014T042736Z-from-bun). This issue is the npm reproduction.

Impact

  • CI is permanently red. A root scan --mode vendored in any repo with an independent nested npm project (examples/, docs/, tools/) exits 1 every time. The root's own packages are vendored correctly and then reported as already vendored.
  • No in-run VEX. scan --mode vendored --vex out.vex.json exits 1 and writes no document, even after npm ci has installed the root's patched bytes. The same shape in hosted mode writes the VEX and exits 0. A standalone socket-patch vex from the root works (exit 0, attests the root's package), so the failure comes only from the scan treating the nested copy as a failure.
  • The remedy is wrong. It says "make sure the package is installed and locked (npm install)". The package is installed and locked, in tools/x. Running npm install at the root or in tools/x changes nothing.
  • The per-path form fails the same way. scan --mode vendored . tools/x vendors tools/x correctly, but its . leg still crawls tools/x/node_modules and fails, so the command exits 1. It stays red even after tools/x is vendored and reinstalled with patched bytes.

Repro

A local mock of the org patch API serves free patches for left-pad@1.3.0 and minimist@1.2.5. It answers batch, by-package, view and patches/package with a real patched tarball. Every socket-patch command gets --api-url <mock> --api-token fake --org test-org.

mkdir -p r/tools/x && cd r && git init -q && echo node_modules/ > .gitignore
echo '{"name":"root","private":true,"dependencies":{"minimist":"1.2.5"}}' > package.json
echo '{"name":"x","private":true,"dependencies":{"left-pad":"1.3.0"}}' > tools/x/package.json
npm install && (cd tools/x && npm install)

socket-patch scan --mode vendored --json --yes   # run 1: partialFailure, exit 1
socket-patch scan --mode vendored --json --yes   # run 2: partialFailure, exit 1 (minimist already_vendored)
npm ci
socket-patch scan --mode vendored --yes --vex out.vex.json --vex-product pkg:npm/root@1.0.0   # exit 1, no out.vex.json
socket-patch vex -O out.vex.json --product pkg:npm/root@1.0.0 --offline                      # exit 0, attests minimist

# hosted control (fresh copy of the same tree)
socket-patch scan --mode hosted --json --yes --patch-server-url <mock>   # success, exit 0

Vendored output (npm 10.9.4, main 37c8918):

status: partialFailure            (exit 1)
  downloaded pkg:npm/left-pad@1.3.0
  downloaded pkg:npm/minimist@1.2.5
  failed  pkg:npm/left-pad@1.3.0 vendor_lock_entry_not_found package-lock.json has no rewritable entry for left-pad@1.3.0 — make sure the package is installed and locked (`npm install`) before vendoring
  applied pkg:npm/minimist@1.2.5

Hosted output for the same tree:

status: success                   (exit 0)
  skipped pkg:npm/left-pad@1.3.0 redirect_unconfirmed no lockfile entry pinning it could be rewritten
  applied pkg:npm/minimist@1.2.5
  W redirect_npm_entry_not_found: no package-lock.json entry for left-pad@1.3.0

Expected vs actual

  • Expected. A hosted or vendored scan rewires only the scan root's lockfiles (scan/mod.rs:1789). A nested project is reached by passing its PATH (docs/configuration.md: "Each PATH in hosted or vendored mode is a project directory"). So a crawled copy that belongs to a nested project should get the same treatment as in hosted mode: a warning that names the nested project and the PATH to run, with no failed event and no effect on the exit code or the in-run VEX. CLI_CONTRACT.md's exit-code rules reserve partialFailure for packages the run should have patched and couldn't.
  • Actual. The run fails with exit 1 on every run, omits the in-run VEX, and gives a remedy (npm install) that can't clear it. scan . tools/x is also red.

Matrix

OS npm / Node socket-patch Reproduces
Linux 10.9.4 / Node 22.22 main 37c8918 yes (×3, incl. --vex and . tools/x)
Linux 12.2.0 / Node 22.22 main 37c8918 yes (×2 runs)
Linux 8.19.4 / Node 22.22 main 37c8918 yes (×2 runs)
Linux 10.9.4 / Node 22.22 v5.0.1 (npm latest) yes
Linux Bun 1.3.0 / 1.3.9 / 1.4.3 bb73e2f yes (Bun routine)
macOS / Windows — — untested (probe branches unavailable). The logic is OS-independent.

First bad: pre-existing. It is in v5.0.1 already.

Suspect code

🤖 Generated with Claude Code

Activity

  1. mikolalysenko commented on Oct 14, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triage: priority:p1 (npm; the Bun report the bug-hunt routine handed over goes through the same scan path). Confirmed on main a3f56f7: in crates/socket-patch-cli/src/commands/scan/mod.rs, policy.judge_nested_roots(...) runs only when the mode is neither hosted nor vendored, so a vendored root scan hands every crawled copy, including ones under a nested project's node_modules, to the root lock backend. The npm backend then turns the missing root entry into the hard vendor_lock_entry_not_found refusal (crates/socket-patch-core/src/vendor/npm_lock.rs), where hosted skips with a warning.

    Not a duplicate: #554 fixed the same nesting question for agent and report-only scans only. No open PR covers it. Unclustered, cluster of one.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 14, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: hosted/vendored scans hand crawled copies from nested independent npm projects to the root lock backend). Branch: agent/fix-vendored-nested-project-copies. Claim-ID: 2026-10-14T18:21:08Z-f2ff7c


    Generated by Claude Code

  3. mikolalysenko commented on Oct 14, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1543


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions