anomalyco / anomalyco/opencode

updater: in-place npm install races exec → ENOEXEC on launch, hang on retry

Open
#41,495 2 comments 0 reactions 1 assignee View on GitHub

@nexxeln is already working on this.

Since Aug 10, 2026.

2.0
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), next channel
  • Active plugins: ~/.config/opencode/plugins/notify.ts

Reproduction

  1. Install via npm install -g @opencode-ai/cli@next (auto-update active).
  2. 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 -g in another terminal — run opencode2.
  3. Observe immediate failure: Error: ENOEXEC: unknown error, posix_spawn '.../bin/opencode2.exe' (raw effect error, no hint that an update is in progress).
  4. 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 starts npm 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 launches opencode2ENOEXEC: 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 list then install); completes 07: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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.