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
- 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 -g in another terminal — run opencode2.
- 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 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 opencode2 → 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 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.
Summary
Launching
opencode2while the auto-updater is runningnpm install -g @opencode-ai/cli@<next>(or launching concurrently with a second instance's update) hitsENOEXEC: 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
/opt/homebrew/lib/node_modules/@opencode-ai/cli),nextchannel~/.config/opencode/plugins/notify.tsReproduction
npm install -g @opencode-ai/cli@next(auto-update active).npm install -gin another terminal — runopencode2.Error: ENOEXEC: unknown error, posix_spawn '.../bin/opencode2.exe'(raw effect error, no hint that an update is in progress).Alternative trigger with worse impact: kill the
npm installmid-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
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).Updater npm log pattern (every cycle: version check then exact-version install):
Full first-failure stack trace:
Additional Context
npm update) will hit it. The next channel makes this common because builds advance several times an hour.npm install -g @opencode-ai/cli@next.