feat(coana): add npm-install + node fallback when dlx fails#1327
Merged
Martin Torp (mtorp) merged 2 commits intoMay 22, 2026
Conversation
In some CI environments, `npx --yes @coana-tech/cli ...` (or the pnpm dlx / yarn dlx equivalents) aborts before Coana itself starts even though the registry, npm, and node all work normally. When that happens, the Socket CLI now retries by installing @coana-tech/cli into a temp directory via `npm install` and invoking it directly through `node`. Two env vars are exposed for support/debugging: - SOCKET_CLI_COANA_FORCE_NPM_INSTALL=1 — skip dlx, go straight to the install path. Useful for validating the fallback end-to-end. - SOCKET_CLI_COANA_DISABLE_NPM_FALLBACK=1 — disable the fallback and restore the previous behavior of returning the dlx error directly. The installed Coana is cached per-version for the lifetime of the process so callers that invoke Coana repeatedly (e.g. `socket fix` iterating per GHSA in PR mode) only pay the install cost once.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Uncaught node spawn errors
- Wrapped the npm-install fallback Coana node spawn in the existing CResult error conversion and added a regression test for fallback node spawn failures.
Or push these changes by commenting:
@cursor push af5c8f716d
Preview (af5c8f716d)
diff --git a/src/utils/dlx.mts b/src/utils/dlx.mts
--- a/src/utils/dlx.mts
+++ b/src/utils/dlx.mts
@@ -316,13 +316,17 @@
message: `npm install fallback failed: ${stderr || cause}`,
}
}
- return await spawnCoanaScriptViaNode(
- scriptPath,
- args,
- finalEnv,
- options,
- spawnExtra,
- )
+ try {
+ return await spawnCoanaScriptViaNode(
+ scriptPath,
+ args,
+ finalEnv,
+ options,
+ spawnExtra,
+ )
+ } catch (e) {
+ return buildDlxErrorResult(e)
+ }
}
/**
diff --git a/src/utils/dlx.test.mts b/src/utils/dlx.test.mts
--- a/src/utils/dlx.test.mts
+++ b/src/utils/dlx.test.mts
@@ -388,5 +388,40 @@
expect(result.message).toContain('npm-install fallback also failed')
expect(result.message).toContain('registry unreachable')
})
+
+ it('surfaces both dlx and node errors when fallback node spawn fails', async () => {
+ mockSpawn.mockImplementation(async (cmd: string, args: string[]) => {
+ if (cmd === 'npm' && args[0] === 'install') {
+ const prefixIdx = args.indexOf('--prefix')
+ const installDir = args[prefixIdx + 1]
+ const pkgDir = path.join(
+ installDir,
+ 'node_modules',
+ '@coana-tech',
+ 'cli',
+ )
+ await fs.mkdir(pkgDir, { recursive: true })
+ await fs.writeFile(
+ path.join(pkgDir, 'package.json'),
+ JSON.stringify({ bin: { coana: 'dist/cli.js' } }),
+ )
+ return { stdout: '', stderr: '' }
+ }
+ throw Object.assign(new Error('node boom'), {
+ code: 127,
+ stderr: 'node failed',
+ })
+ })
+
+ const result = await spawnCoanaDlx(['run', '.'], 'acme', {
+ coanaVersion: nextVersion(),
+ })
+
+ expect(result.ok).toBe(false)
+ expect(result.message).toContain('Coana command failed')
+ expect(result.message).toContain('npx aborted')
+ expect(result.message).toContain('npm-install fallback also failed')
+ expect(result.message).toContain('node failed')
+ })
})
})You can send follow-ups to the cloud agent here.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 1549f69. Configure here.
Benjamin Barslev Nielsen (barslev)
approved these changes
May 22, 2026
Address two issues with the npm-install + node fallback:
1. spawnCoanaViaNpmInstall now wraps the node spawn in try/catch and
converts any thrown spawn error into a CResult, mirroring the
SOCKET_CLI_COANA_LOCAL_PATH branch and the dlx try/catch. Previously a
Coana process failure (after a successful install) could throw past
spawnCoanaDlx into callers.
2. Add a shouldFallbackOnDlxError heuristic so we only retry when the
dlx launcher failed before Coana ran. The previous behavior retried
on any rejection, which would re-run Coana for real analysis
failures, wasting time and producing misleading logs. The heuristic:
- Captured stderr contains "Coana CLI version" → Coana booted, do
not retry.
- Spawn-level error (string code like ENOENT) → retry.
- Killed by signal, or numeric exit code ≥ 128 → retry (covers the
observed exit 249 / 254 launcher cases).
- Small integer exit codes with no banner in captured stderr →
default to NOT retrying (Coana's own exit codes are small
integers).
Adds four tests covering the new branches: small-integer exit codes
skip the fallback, captured Coana banner skips the fallback,
ENOENT-style errors trigger the fallback, signal kills trigger the
fallback.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
npx --yes @coana-tech/cli ...(or the equivalentpnpm dlx/yarn dlx) aborts before Coana itself starts, the Socket CLI now retries by installing@coana-tech/cliinto a temp directory vianpm installand invoking it directly throughnode. The registry, npm, and node all work normally in the environments that hit this — only the dlx-driven launch is broken.socket scan reach,socket fixdiscovery,socket fixper-GHSA loop) inherit the fallback transparently — the change is isolated tospawnCoanaDlxinsrc/utils/dlx.mts.Mapfor the lifetime of the process, so PR mode (which calls Coana once per GHSA) only pays the install cost once.Env vars
For support/debugging only — not documented in CHANGELOG:
SOCKET_CLI_COANA_FORCE_NPM_INSTALL=1— skip dlx, go straight to the install path. Useful for validating the fallback end-to-end without needing dlx to actually fail.SOCKET_CLI_COANA_DISABLE_NPM_FALLBACK=1— disable the fallback and restore the previous behavior of returning the dlx error directly.Test plan
pnpm check:tsc— cleanpnpm check:lint— cleanpnpm test:unit src/utils/dlx.test.mts— 11/11 pass (added 5 new tests covering: fallback fires on dlx error, install caching across calls, DISABLE env skips fallback, FORCE env skips dlx, both errors surface when install also fails)pnpm test:unitfor the 4 Coana-caller test files (handle-scan-reach,exclude-paths,handle-create-new-scan,handle-fix-limit) — 47/47 passSOCKET_CLI_COANA_FORCE_NPM_INSTALL=1 socket scan reach <project>against a small npm project — observednpm installof@coana-tech/cli@15.3.4into asocket-coana-*tmpdir, followed by Coana running vianodeand completing the reachability analysis successfully (.socket.facts.jsonwritten, expected vuln counts produced).Note
Medium Risk
Adds a new execution path that installs and runs
@coana-tech/clivianpm install+node, which changes runtime behavior and introduces new failure/perf modes in environments where dlx is flaky.Overview
spawnCoanaDlxnow retries Coana execution whennpx/pnpm dlx/yarn dlxfails by installing@coana-tech/cliinto a temp dir vianpm installand running it vianode, caching the resolved CLI entrypoint per version for the lifetime of the process.Adds support/debug env toggles to force the npm-install path (
SOCKET_CLI_COANA_FORCE_NPM_INSTALL) or disable the fallback (SOCKET_CLI_COANA_DISABLE_NPM_FALLBACK), improves error reporting by surfacing both dlx and fallback failures, and adds unit tests covering fallback behavior and caching.Reviewed by Cursor Bugbot for commit 1549f69. Configure here.