Repository navigation
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/[email protected]; 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/[email protected]
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.
Activity
hanipcode commentedon Aug 12, 2026
I encountered the same failur on macOS arm64 with Node v22.20.0 through NVM and a global npm installation of
@opencode-ai/cli.Additional Context
At 09:42:36, starting
opencode2failed while launching the background service:bjorntechTobbe commentedon Aug 28, 2026
I can reproduce a self-update race in the global npm-installed
opencode2on macOS arm64.Environment
@opencode-ai/cli/opencode2.exeshimRepro
--standalonesessions at about the same time.Timeline
10:53:07one CLI launch starts from$HOME/.nvm/versions/node/v24.15.0/bin/opencode2.exeand is already on0.0.0-beta-18414.10:53:09-10:53:11several server-side sessions (0.0.0-beta-18371,0.0.0-beta-18381,0.0.0-beta-18387) all detect0.0.0-beta-18414and spawnnpm install --global @opencode-ai/[email protected]concurrently.updated OpenCode from 0.0.0-beta-18387 to 0.0.0-beta-18414), while others fail.Sanitized error excerpts
Observed final state
opencode2no longer resolved, while already-running OpenCode processes remained alive.current=0.0.0-beta-18414 latest=0.0.0-beta-18414.automatic update skipped: installation method not found.Correlation / impact
This looks like a race in the shared self-update path, not workspace-specific behavior. It makes updates nondeterministic and can remove the globally installed CLI while existing processes continue running.
neriousy commentedon Oct 22, 2026
Closing this, since most of it is fixed on dev:
/updateandopencode upgrade) holds a cross-processcli-upgradelock for the whole install (feat(cli): check for updates every 10 minutes #52552).npm install -g(fix(cli): keep updates client-owned #49577).notify(fix(tui): clarify update notifications #47372). OpenCode only replaces itself in the background if you opt in with"update": "auto".One gap remains: if you launch
opencodewhile npm is replacing the binary, starting the service can still fail with a rawENOEXEC. npm replaces global packages in place, so we can't make that swap atomic. If this still bites you on a current build, comment here and we'll reopen it.