[agent] Found by the scheduled npm bug-hunt routine (ledger #302).
Summary
socket-patch apply exits 1 when every manifest patch targets a package that npm skipped on purpose on this host, such as a platform-specific optionalDependencies entry like fsevents (macOS only) or @esbuild/<os>-<cpu>. The tree is already in its correct end state: the package is in the lock with optional: true and an os/cpu filter that excludes this host, so there's nothing to patch. But apply still prints Error: The targeted manifest patch matched no installed package and fails.
Because socket-patch setup wires apply --silent into postinstall and dependencies, this makes npm ci and npm install fail on every OS that doesn't install that package. A team that records a patch for fsevents on a Mac breaks its Linux CI. A team that patches @esbuild/linux-x64 in Linux CI breaks every macOS and Windows developer's npm install.
The result depends on what else is in the manifest. Add one patch for a package that is installed, and the same unmatched optional package downgrades to a warning with exit 0.
Impact
- The installs fail (
npm error command failed … socket-patch apply --silent --ecosystems npm, exit 1) on npm 8, 10, 11 and 12 as soon as the only patch in the manifest is for a platform-skipped optional dependency.
- Platform-gated optional packages are where many native or binary CVEs live (
fsevents, @esbuild/*, @rollup/rollup-*, @swc/core-*, @next/swc-*), so a cross-platform team hits this on its first such patch.
- The released 4.0.0 and 3.3.0 behave the same way, so this isn't a regression.
Repro (Linux, main f6b7fb9)
mkdir app && cd app
echo '{"name":"app","version":"1.0.0","private":true,"dependencies":{"chokidar":"3.6.0"}}' > package.json
npm install # [email protected] is in the lock (optional, os: darwin) but not installed
# record a patch for pkg:npm/[email protected], as `socket-patch get`/`scan` on a Mac would; here
# it's hand-staged: .socket/manifest.json with package/fsevents.js before/after hashes + blobs
socket-patch setup --yes # postinstall/dependencies: npx @socketsecurity/socket-patch apply --silent --ecosystems npm
rm -rf node_modules && npm ci
# npm error command sh -c socket-patch apply --silent --ecosystems npm
# npm error Error: The targeted manifest patch matched no installed package:
# npm error - pkg:npm/[email protected]
# exit 1
socket-patch apply --offline --json # status partialFailure, events [skipped/package_not_installed], exit 1
With a second patch for an installed package (e.g. is-glob) in the same manifest, apply --silent patches it and exits 0: the fsevents entry becomes a warning.
Expected vs actual
Expected: a manifest entry whose package the project lock resolves, but which npm deliberately didn't install on this host (an optional entry whose os/cpu/libc excludes it, or more generally any lockfile-only entry), is a calm skip (skipped / package_not_installed) that never fails the run. That's exactly what CLI_CONTRACT.md promises for the same situation in scan --apply: it "partitions lockfile-only patches out BEFORE download (calm skipped/package_not_installed records — never an error exit …)". rollback already treats a not-installed package as satisfying its end state (CLI_CONTRACT.md, rollback results). The install hook that setup wires should never fail an install whose tree is correct.
Actual: apply (the command the setup hook runs) exits 1 with an error whenever no targeted patch matched an installed package, regardless of whether the lock shows the package was skipped on purpose.
| OS |
npm |
only-other-platform patches: apply / npm ci with the setup hook |
host + other-platform patches: apply |
| Linux |
8.19.4 |
exit 1 / exit 1 (and npm install exit 1) |
— |
| Linux |
10.9.7 |
exit 1 / exit 1 (and npm install exit 1) |
— |
| Linux |
11.20.0 |
exit 1 / exit 1 (and npm install exit 1) |
exit 0, warning only |
| Linux |
12.1.0 |
exit 1 / exit 1 (and npm install exit 1) |
— |
| Linux (ubuntu-latest) |
10.9.7 (probe) |
exit 1 / exit 1 |
exit 0, host package patched |
| Linux (ubuntu-latest) |
12.1.0 (probe) |
exit 1 / exit 1 |
exit 0, host package patched |
| macOS (macos-latest, arm64) |
10.9.7 (probe) |
exit 1 / exit 1 |
exit 0, host package patched |
| macOS (macos-latest, arm64) |
12.1.0 (probe) |
exit 1 / exit 1 |
exit 0, host package patched |
| Windows (windows-latest) |
10.9.7 (probe) |
exit 1 / exit 1 |
exit 0, host package patched |
| Windows (windows-latest) |
12.1.0 (probe) |
exit 1 / exit 1 |
exit 0, host package patched |
The local Linux cells use [email protected] via [email protected], with the main build in the hook. The probe cells use [email protected] with patches for the two @esbuild/* platform packages the runner doesn't install, and the host's own package in column 3. The released 4.0.0 and 3.3.0 also exit 1 on the Linux fsevents repro.
Suspect code
crates/socket-patch-cli/src/commands/apply.rs:2176-2181: none_matched (no targeted purl matched an installed package) sets has_errors = true, without consulting the lockfile.
crates/socket-patch-cli/src/commands/apply.rs:1785-1810: the same decision on the empty-tree path (success: unmatched.is_empty()).
- For comparison,
scan --apply's lockfile-only partition (CLI_CONTRACT.md "Lockfile supplement (v3.4)") already knows these packages are lockfile-resolved, and a calm skip there doesn't change the exit code.
Probe run: https://github.com/SocketDev/socket-patch/actions/runs/36796693378 (all 6 jobs: RESULT only-other … apply_silent_exit=1 … npm_ci_exit=1, RESULT host-plus-other … apply_silent_exit=0 host_patched=1).
[agent] Found by the scheduled npm bug-hunt routine (ledger #302).
Summary
socket-patch applyexits 1 when every manifest patch targets a package that npm skipped on purpose on this host, such as a platform-specificoptionalDependenciesentry likefsevents(macOS only) or@esbuild/<os>-<cpu>. The tree is already in its correct end state: the package is in the lock withoptional: trueand anos/cpufilter that excludes this host, so there's nothing to patch. Butapplystill printsError: The targeted manifest patch matched no installed packageand fails.Because
socket-patch setupwiresapply --silentintopostinstallanddependencies, this makesnpm ciandnpm installfail on every OS that doesn't install that package. A team that records a patch forfseventson a Mac breaks its Linux CI. A team that patches@esbuild/linux-x64in Linux CI breaks every macOS and Windows developer'snpm install.The result depends on what else is in the manifest. Add one patch for a package that is installed, and the same unmatched optional package downgrades to a warning with exit 0.
Impact
npm error command failed … socket-patch apply --silent --ecosystems npm, exit 1) on npm 8, 10, 11 and 12 as soon as the only patch in the manifest is for a platform-skipped optional dependency.fsevents,@esbuild/*,@rollup/rollup-*,@swc/core-*,@next/swc-*), so a cross-platform team hits this on its first such patch.Repro (Linux, main
f6b7fb9)With a second patch for an installed package (e.g.
is-glob) in the same manifest,apply --silentpatches it and exits 0: the fsevents entry becomes a warning.Expected vs actual
Expected: a manifest entry whose package the project lock resolves, but which npm deliberately didn't install on this host (an optional entry whose
os/cpu/libcexcludes it, or more generally any lockfile-only entry), is a calm skip (skipped/package_not_installed) that never fails the run. That's exactly what CLI_CONTRACT.md promises for the same situation inscan --apply: it "partitions lockfile-only patches out BEFORE download (calmskipped/package_not_installedrecords — never an error exit …)".rollbackalready treats a not-installed package as satisfying its end state (CLI_CONTRACT.md,rollbackresults). The install hook thatsetupwires should never fail an install whose tree is correct.Actual:
apply(the command thesetuphook runs) exits 1 with an error whenever no targeted patch matched an installed package, regardless of whether the lock shows the package was skipped on purpose.apply/npm ciwith thesetuphookapplynpm installexit 1)npm installexit 1)npm installexit 1)npm installexit 1)The local Linux cells use
[email protected]via[email protected], with the main build in the hook. The probe cells use[email protected]with patches for the two@esbuild/*platform packages the runner doesn't install, and the host's own package in column 3. The released 4.0.0 and 3.3.0 also exit 1 on the Linux fsevents repro.Suspect code
crates/socket-patch-cli/src/commands/apply.rs:2176-2181:none_matched(no targeted purl matched an installed package) setshas_errors = true, without consulting the lockfile.crates/socket-patch-cli/src/commands/apply.rs:1785-1810: the same decision on the empty-tree path (success: unmatched.is_empty()).scan --apply's lockfile-only partition (CLI_CONTRACT.md "Lockfile supplement (v3.4)") already knows these packages are lockfile-resolved, and a calm skip there doesn't change the exit code.Probe run: https://github.com/SocketDev/socket-patch/actions/runs/36796693378 (all 6 jobs:
RESULT only-other … apply_silent_exit=1 … npm_ci_exit=1,RESULT host-plus-other … apply_silent_exit=0 host_patched=1).