fix(cli): upgrade aborts with uncaught ENOEXEC on macOS when a package manager on PATH has no shebang

未关闭 适合新手
#62,516 2 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
72/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
bun, typescript
领域
cli

调研方向

从 packages/cli/src/services/updater.ts 第 233 行附近的包管理器探测开始。然后阅读 packages/util/src/cross-spawn-spawner.ts 第 377 行附近的 launch(...),以及 packages/cli/test/updater-install.test.ts 第 298 行附近现有的包管理器不可用测试。重现同步 ENOEXEC 错误,并检查探测错误的处理方式。完成标准是:即使包管理器不可用,upgrade 和 uninstall 仍能继续;可用的包管理器仍能被检测到。

由索引模型根据 Issue 内容生成。

描述

reproduced

Description

On macOS, opencode upgrade and opencode uninstall crash with a stack trace when a package manager on PATH is an executable script without a shebang, even one the install does not use.

OpenCode version

@opencode/cli 2.2.6 (latest on npm) in an npm global prefix. 2.2.5 behaves the same when run here. The first report, supplied rather than rerun, came from 2.2.0 with /opt/homebrew/bin/pnpm.

Plugins

None

Operating System

macOS 27.2 arm64, with npm 12.2.1, Node 26.12.1, Bun 1.4.4 and pnpm 12.16.1. Linux and Windows are not tested, so reach beyond macOS is unknown. pnpm's launcher comment says glibc's execvp retries this file under a shell, "Apple's libc does not, so on macOS a program that spawns this path itself gets ENOEXEC", and Windows cannot reach it at all.

Terminal

CLI runs used a non-interactive shell with TERM=dumb. The TUI run used a pty with TERM=xterm-256color.

Summary

Install-method detection probes npm, pnpm, bun, yarn and vp, so an npm install of OpenCode fails on a pnpm it does not use. One way to get this state is a plain npm install -g pnpm on npm 12, which blocks pnpm's install scripts and leaves its shebang-less launcher in place. That launcher still works from a shell.

Steps to reproduce

The exports keep this in throwaway directories. Executed as written on macOS:

export HOME=$(mktemp -d) NPM_CONFIG_PREFIX=$(mktemp -d)
export PATH="$NPM_CONFIG_PREFIX/bin:$PATH"
npm install -g @opencode/[email protected] --allow-scripts=@opencode/cli
npm install -g [email protected]   # npm 12 blocks pnpm's install scripts
pnpm --version                # prints 12.16.1 from a shell
opencode upgrade

A smaller trigger gives the same error: an executable file named pnpm or yarn containing only echo 12.16.1, first on PATH.

Expected behavior

Detection treats a package manager it cannot run as "not this one" and moves on. method() recovers probe failures with Effect.orElseSucceed, the test "method detection tolerates unavailable package managers" states that intent, and a missing executable already behaves this way. For this npm install, the upgrade should print Using method: npm and continue.

Actual behavior

Exit status 1, version unchanged. Stack frames trimmed:

┌  Upgrade
Error: ENOEXEC: unknown error, posix_spawn '<prefix>/bin/pnpm'
    at spawn (node:child_process:727:35)
    at AppProcess.run (/$bunfs/root/chunk-cd0exa2g.js:7:1712)
    at cli.upgrade (/$bunfs/root/chunk-cd0exa2g.js:7:21641)

Other results on the latest release:

Condition Result
No shebang-less manager on PATH, opencode upgrade Using method: npm, exit 0
Shebang-less pnpm, opencode upgrade 2.2.5 --method npm Installs 2.2.5, exit 0
Shebang-less file named yarn, opencode upgrade Same ENOEXEC for yarn
Shebang-less pnpm, opencode uninstall, with or without --dry-run Same ENOEXEC
pnpm reinstalled with --allow-scripts=pnpm (the file becomes Mach-O), opencode upgrade and opencode uninstall --dry-run Both report npm, exit 0

With both Node and Bun, child_process.spawn throws ENOEXEC synchronously on this file, while a missing file emits an asynchronous error event. launchProcess handles only the event, so the throw becomes an Effect defect, which orElseSucceed does not recover. Running AppProcess.run(...).pipe(Effect.orElseSucceed(...)) with the real spawner from dev showed the split: the missing file recovered and the shebang-less file escaped as Error: ENOEXEC.

pnpm treats this launcher as a degraded state and tells users to reinstall, so that install is worth repairing. An unusable pnpm should still not block upgrading an npm install, and the stack trace names no next step.

Evidence

  • Expected source: the probe recovery at updater.ts#L233 and the test at updater-install.test.ts#L298, both on dev.
  • Failure source: the output above from the steps above, and the launch(...) call at cross-spawn-spawner.ts#L377. updater.ts, cross-spawn-spawner.ts, process.ts and upgrade.ts are identical between the latest release tag and that dev commit.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

Restoration check

On the same setup with a build containing the fix, opencode upgrade prints Using method: npm and continues with no ENOEXEC, and opencode uninstall --dry-run reaches Dry run - no changes made. As a control, a working pnpm or yarn that owns the install must still be detected as that method.

References

  • #58685 and open PR #58722 concern the same synchronous spawn throw in a different failure: EPERM from git on Windows failing a session step through Snapshot.capture. That PR also wraps this launch(...) call. I applied the same try/catch locally on dev and the probe above then recovered for the shebang-less file. I did not build a CLI with it, so opencode upgrade itself is untested with that change.
  • #57579 is a different symptom on Windows: detection finishes and reports that it could not detect the method.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: On macOS, opencode upgrade and opencode uninstall fail on every run for a machine in this state, with no next step. Only machines with such a package manager on PATH are affected, the installed OpenCode keeps working, and the TUI started normally in the one 25-second run tried, though its update check's outcome is not established.
  • Workaround status: available
  • Workaround basis: opencode upgrade <version> --method npm skips detection and installed the requested version with the shebang-less pnpm on PATH; on 2.2.5 it leaves the package's postinstall stub, which #51554 fixed for the latest release. npm install -g [email protected] --allow-scripts=pnpm removes the trigger, and both opencode upgrade and opencode uninstall --dry-run then pass. opencode uninstall has no --method flag.
主要语言
TypeScript
星标
224k
派生
30k
平均合并
4 小时 44 分钟
30 天内合并 PR
513

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

anomalyco/opencode 的其他 Issue

查看 anomalyco/opencode 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。