cli: manual recovery `node postinstall.mjs` fails on Windows - `npm_execpath` unset causes silent `spawnSync("npm")` ENOENT
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 73/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- node.js, typescript
- Domain
- build-system, cli
Research direction
Start with packages/cli/postinstall.mjs, especially the npm executable selection and installPackage() handling described in the report. Reproduce by running node postinstall.mjs outside an npm lifecycle on Windows and inspect the spawn result. Done means manual recovery can invoke npm successfully and failures expose the underlying error.
Written by the indexing model from the issue text.
Description
Summary
The manual recovery step recommended in #55888 - running node postinstall.mjs inside the installed @opencode/cli package - cannot work on Windows when invoked outside an npm lifecycle context.
postinstall.mjs only resolves the npm CLI correctly when npm_execpath is set. Otherwise it falls back to spawnSync("npm", ...), which always fails with ENOENT on Windows because the npm shim is npm.cmd.
The failure is silent: installPackage() treats a non-zero status as a failed install and continues, so the user only ever sees the generic Failed to install OpenCode. Try manually installing ... message, with no npm output and no hint of the real cause.
Environment
- opencode version: 2.0.31
- OS: Windows 11 (win32 x64)
- Node: v24.18.0
- npm: 11.16.0
- Install method: global npm install (
npm i -g @opencode/cli) npm_execpath: unset (becausepostinstall.mjswas run directly withnode)
Root cause
packages/cli/postinstall.mjs:
// Windows cannot spawn npm's .cmd shim directly, so reuse the npm CLI that runs this script.
const npm = process.env.npm_execpath?.endsWith("npm-cli.js")
? [process.execPath, process.env.npm_execpath]
: ["npm"]
The comment states the intent, but the ["npm"] fallback is unreachable on Windows. childProcess.spawnSync("npm", ...) without shell: true does not resolve .cmd shims through PATHEXT.
Reproduction
PS> cd $env:APPDATA\npm\node_modules\@opencode\cli
PS> node postinstall.mjs
Failed to install OpenCode. Try manually installing "@opencode/cli-windows-x64" or "@opencode/cli-windows-x64-baseline".
# exit code 1, ~0.4s, no npm output
Evidence
Same machine, same session:
npm_execpath = undefined
spawnSync("npm", ...) -> status = null error = ENOENT
spawnSync("npm.cmd", ...) -> status = null error = EINVAL
spawnSync(process.execPath, [npmCliJs, "--version"]) -> status = 0 stdout = "11.16.0"
Setting npm_execpath makes the identical invocation succeed:
PS> $env:npm_execpath = "C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js"
PS> node postinstall.mjs
# exit code 0
Suggested fix
Resolve the npm CLI robustly instead of assuming it is directly spawnable:
- resolve
npm-cli.jsrelative toprocess.execPath(../node_modules/npm/bin/npm-cli.js), or - fall back to
npm.cmdwithshell: true, or - resolve through
PATHEXT
Separately, consider surfacing result.error when installPackage fails - the current message hides ENOENT entirely, which makes this hard to diagnose.
Additional context
This machine was in the state described by #55888 (bin/opencode.exe left as the 229-byte placeholder). Because node postinstall.mjs was the suggested recovery and it fails on Windows, the install could not be repaired without manually setting npm_execpath.
Related
- #55888 (closed) - recommends
node postinstall.mjsas manual recovery - #51553 (open) - npm 12 upgrade leaves the Windows executable stub
- Dominant language
- TypeScript
- Stars
- 215k
- Forks
- 28.9k
- Avg merge
- 8h 29m
- Merged PRs (30d)
- 252
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from anomalyco/opencode
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
anomalyco/opencode#56352 · 2 comments ·
Maintainers usually reply within 1 day
-
web / desktop client: model tooltip shows "No reasoning" for models that expose reasoning variantsOpenpending close triaging
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
anomalyco/opencode#56317 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 75/100
anomalyco/opencode#56277 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
anomalyco/opencode#56251 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 75/100
anomalyco/opencode#56179 · 1 comment ·
Maintainers usually reply within 1 day
All issues in anomalyco/opencode
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 90/100
chrisparsons83/flexspotff#215 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
vitest-dev/vitest#11633 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
Maintainers usually reply within 1 day