anomalyco / anomalyco/opencode
updater: in-place npm install races exec → ENOEXEC on launch, hang on retry
@nexxeln is already working on this.
Since Aug 10, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- Avg merge
- 7h 2m
- Merged PRs (30d)
- 384
Description
Summary
Launching opencode2 while the auto-updater is running npm install -g @opencode-ai/cli@<next> (or launching concurrently with a second instance's update) hits ENOEXEC: unknown error, posix_spawn '.../bin/opencode2.exe' because the updater replaces the live executable in place, non-atomically. A retry then hangs until the install finishes, and a killed install leaves the CLI unable to start at all until the next update cycle.
Environment
- opencode version: 0.0.0-next-17082 at failure (current 0.0.0-next-17086; next channel advances several builds/hour)
- OS: macOS Darwin 25.5.0 (arm64)
- Terminal: ghostty (TERM=xterm-ghostty, COLORTERM=truecolor)
- Shell: /bin/zsh
- Install/channel: npm global install (
/opt/homebrew/lib/node_modules/@opencode-ai/cli),nextchannel - Active plugins:
~/.config/opencode/plugins/notify.ts
Reproduction
- Install via
npm install -g @opencode-ai/cli@next(auto-update active). - While an update is in flight — either the updater's own background cycle (observed running on its own roughly every 13–20 min on the next channel) or a user-initiated
npm install -gin another terminal — runopencode2. - Observe immediate failure:
Error: ENOEXEC: unknown error, posix_spawn '.../bin/opencode2.exe'(raw effect error, no hint that an update is in progress). - Retry: process hangs ("Starting background server..." with no progress) until the install completes, then succeeds.
Alternative trigger with worse impact: kill the npm install mid-reify (SIGTERM/Ctrl-C). The package is left half-replaced; subsequent launches keep failing with ENOEXEC until the background updater happens to re-run and finish the install.
Expected Behavior
- The updater should replace the executable atomically (install to a staging path then rename, or use versioned binary paths), so a spawn never observes a half-written binary.
- The launcher should detect a transiently unexecutable target (file changing, ENOEXEC) and either retry briefly or print a clear "updating, retry in a moment" message instead of a raw error and a hang.
Actual Behavior
Timeline from npm debug logs and file mtimes (all times local):
07:13:26— auto-updater startsnpm install -g @opencode-ai/cli@0.0.0-next-17082; process is terminated (SIGTERM) mid-reify (~/.npm/_logs/2026-08-10T06_13_26_476Z-debug-0.log:88), leaving the package half-moved.07:13:31— user launchesopencode2→ENOEXEC: unknown error, posix_spawn '/opt/homebrew/lib/node_modules/@opencode-ai/cli/bin/opencode2.exe'(file present but not a valid executable).07:13:50— updater re-runs (npm listtheninstall); completes07:13:51(binary mtime = 155 MB valid arm64 Mach-O).- Second launch hung through this window; third succeeded.
Updater npm log pattern (every cycle: version check then exact-version install):
verbose title npm list @opencode-ai/cli
verbose title npm install @opencode-ai/cli@0.0.0-next-17082
error process terminated / signal SIGTERM (first cycle, mid-reify)
Full first-failure stack trace:
ERROR (#1): Error: Failed to start server
at service.ensure (src/services/server-connection.ts:66:53)
at cli.server-connection.resolve (src/commands/handlers/default.ts:22:44)
[cause]: Error: ENOEXEC: unknown error, posix_spawn '/opt/homebrew/lib/node_modules/@opencode-ai/cli/bin/opencode2.exe'
at try (../client/src/effect/service.ts:71:23)
Additional Context
- Occurred once here, but the race is inherent: any launch concurrent with an in-flight global install (another terminal's launch, the background poller, or a manual
npm update) will hit it. The next channel makes this common because builds advance several times an hour. - Not related to network: registry fetches all returned HTTP 200 at the time of failure.
- Workaround that helped: wait ~20–30 s and relaunch; if the CLI still fails with ENOEXEC, manually re-run
npm install -g @opencode-ai/cli@next. - Root-cause sketch: npm's global reify replaces files in place (renames package dir to a staging name, extracts new one); the updater spawns/execs that same path while it is being replaced, and nothing retries on ENOEXEC.
Contributor 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.
Assessment
This issue has not been assessed yet.