Skip to content

cli: upgrade --method curl fails on Windows (mangled backslash path passed to bash) #50924

Closed

Description

@yveming

Summary

opencode upgrade --method curl fails on native Windows: the downloaded install script is written to a Windows path (C:\Users\<user>\.cache\opencode\update-XXXX\install) but executed through bash with the backslashes mangled, so bash reports No such file or directory.

Environment

  • opencode version: 2.0.14 (upgrading to 2.0.15)
  • OS: Windows_NT 10.0.26200 (win32 x64)
  • Terminal: TERM=xterm-256color (TERM_PROGRAM/COLORTERM unset)
  • Shell: cmd.exe (C:\WINDOWS\system32\cmd.exe), PowerShell 5.1; bash on PATH resolves to WSL C:\Windows\System32\bash.exe
  • Install/channel: curl method, binary at C:\Users\ming\.opencode\bin\opencode.exe
  • Active plugins: none found in global or project config

Reproduction

  1. On native Windows, install opencode with the curl method so the binary lives at %USERPROFILE%\.opencode\bin\opencode.exe.
  2. Ensure bash on PATH is WSL's C:\Windows\System32\bash.exe (or any bash that cannot parse C:\ paths).
  3. Run opencode upgrade 2.0.15 --method curl.

Expected Behavior

The install script at C:\Users\<user>\.cache\opencode\update-XXXX\install is executed successfully and opencode is upgraded to 2.0.15.

Actual Behavior

Upgrade fails immediately with:

┌  Upgrade
│
●  Using method: curl
│
●  From 2.0.14 → 2.0.15
│
■  Upgrade failed
│
■  The OpenCode installer failed
│
│  /bin/bash: C:Usersming.cacheopencodeupdate-29WiNminstall: No such file or directory
│
│  Command   opencode upgrade 2.0.15 --method curl
│  Retry     Fix the issue above, then run opencode upgrade again.

The path shown by bash has had every backslash stripped (C:\Users\ming\.cache\opencode\update-29WiN\install → C:Usersming.cacheopencodeupdate-29WiNminstall), indicating the path is passed to bash unquoted, so backslashes are consumed as escape characters. Additionally, the bash found on PATH is WSL's bash, which cannot open C:\... paths at all (it would need /mnt/c/...), so even a correctly quoted Windows path would fail when resolved this way.

Additional Context

  • Binary evidence from 2.0.14 (opencode.exe): the curl upgrade path is hardcoded as two steps — (1) download https://opencode.ai/v2/install to <cache>/update-XXXX/install (error title "Could not download the OpenCode installer"), then (2) spawn ["bash", <cached install script>, "--version", <target>, "--no-modify-path"] (error title "The OpenCode installer failed"). There is no PowerShell/cmd fallback, so on Windows the method depends entirely on bash being resolvable.
  • The mangled path indicates the script path passes through an unquoted shell parse before reaching bash: every backslash in C:\Users\ming\.cache\opencode\update-XXXX\install was consumed as an escape character. This layer is independent of which bash binary is ultimately invoked.
  • If no bash exists on PATH at all, step 2 fails even earlier with a spawn error (ENOENT / "The system cannot find the file specified") under the same "The OpenCode installer failed" title — i.e., removing bash does not make the command succeed; it only changes the failure mode.
  • The temporary directory update-XXXX is cleaned up after the failure, so there is no leftover state.
  • The v2 install script (https://opencode.ai/v2/install) works when run manually in Git Bash, since it downloads @opencode/[email protected] from npm (opencode upgrade --method curl appears to take a different path that breaks on Windows).
  • The v2.0.15 GitHub release has no binary assets (source only), so a curl-based upgrade must go through the v2 install script / npm distribution rather than releases/download.
  • Reproducible consistently (path handling is platform-specific, not intermittent).

Activity

  1. Tsuki-hash commented on Sep 24, 2026

    @Tsuki-hash

    Root cause is which bash gets spawned, not how the path is written. On Windows 11 26200: Git Bash runs a script at C:\Users\<name>\.cache\opencode\update-XXXX\install without complaint (exit 0), so the backslashes are not the problem. The WSL entry point at C:\Windows\System32\bash.exe strips them and exits 127 with exactly the message in this issue. Converting the path, as suggested here and in #50950, would make it worse: WSL bash then does run the script, and because install installs whatever uname -s reports, it fetches a Linux binary into the Windows install directory.

    Fixing it by asking each candidate shell which system it runs on and accepting only MINGW/MSYS/CYGWIN — the same classification install uses — plus honoring the already-documented OPENCODE_GIT_BASH_PATH, which the updater ignored. Claiming this unless someone is mid-flight; #50886 is the same root cause seen from the "bash is not installed at all" side, so whoever is on it: the shell choice is the thing to fix.

    @yveming thanks for the log evidence, it is what pinned this down.

    Two corrections to what I wrote above. (1) I named C:\Windows\System32\bash.exe as the WSL entry point; that file does exist on my machine, but the resolving stub varies by install - @jpgomezv documented below that on his it is ...\WindowsApps\bash.exe with no System32 one at all. The rule to state is "the WSL bash answers whenever Git is not on PATH", not one fixed path. (2) I speculated that #50950's backslash-intact wording meant the script had been deleted between download and exec. @jpgomezv has since reproduced that exact text with the file verified present, so both wordings come from a WSL bash and no second cause is needed. #51038 is intended to cover both reports.

  2. jpgomezv commented on Sep 25, 2026

    @jpgomezv

    Confirming the shell-choice diagnosis with diagnostics from this machine, plus one correction to the #50950 caveat.

    bash resolves to WSL here, not Git Bash

    where.exe bash  -> C:\Users\gomez\AppData\Local\Microsoft\WindowsApps\bash.exe
    bash --version  -> GNU bash, version 5.3.9(1)-release (x86_64-pc-linux-gnu)
    

    Git Bash is installed at C:\Program Files\Git\bin\bash.exe, but only Git\cmd is on PATH, so bash falls through to the WSL alias. OPENCODE_GIT_BASH_PATH is unset here. (FYI C:\Windows\System32\bash.exe does not exist on this machine — the stub is the WindowsApps alias.)

    #50950 does not need a second cause

    I reproduced that exact text with the script present:

    bash "C:\Users\gomez\.cache\opencode\zz-test-bash\install" --version
    /bin/bash: C:\Users\gomez\.cache\opencode\zz-test-bash\install: No such file or directory
    

    The file was verified present before and after. WSL bash words a Windows path like that with the backslashes intact, so nothing has to remove it between download and exec. Nothing on this machine touches .cache\opencode\update-*.

    Small nuance for the fix

    A Git-Bash-style conversion (/c/Users/...) also fails in WSL bash with the same message — only /mnt/c/... would actually run it. So the MINGW/MSYS/CYGWIN guard is the right fix, but a naive conversion fails safe rather than installing a Linux binary.

    Reproduced on 2.0.12→2.0.14, 2.0.14→2.0.15 and 2.0.15→2.0.16 if you want another tester.

  3. Tsuki-hash commented on Sep 25, 2026

    @Tsuki-hash

    @jpgomezv 两条都收下:固定路径那条(你那台是 WindowsApps 别名,不是 System32)和 #50950 的"文件被删"猜测(你在脚本在位的情况下复现出同样文本,直接排除)。我已经改掉自己在 #50924 上的说法,并把 #51038 描述里那句 caveat 换成了这段更正。

    你说的 /c/Users/... 细节和我的观察一致:我这台机器上 Git Bash 对原生 C:\... 参数是直接接受的(exit 0),所以问题从来不在路径写法,而在选中了哪个 shell —— 这也是 #51038 用候选 shell 自己的 uname -s 做过滤、完全不做路径转换的原因。

    愿意做这个测试者,正好补上我缺的那块。改动在 packages/cli/src/services/updater.ts,分支 Tsuki-hash:windows-installer-shell(PR #51038,base v2)。我这边没法端到端验证:本机没装 Bun,而且真跑升级会动到我自己的安装。你机器上值得看的两个点是:Git 只在 Git\cmd 进 PATH 的情况下,安装脚本是否由 C:\Program Files\Git\bin\bash.exe 执行;以及把 Git 从 PATH 完全摘掉后,是否得到那条具名错误(提示装 Git for Windows 或设 OPENCODE_GIT_BASH_PATH),而不是现在的 127。

  4. EloyRD commented on Sep 25, 2026

    @EloyRD

    Reproduced on Windows with OpenCode v2.0.15 while upgrading to v2.0.16.

    Installation:
    C:\Users\USERNAME.opencode\bin\opencode.exe

    Command:
    opencode update

    Error:
    /bin/bash: C:UsersUSERNAME.cacheopencodeupdate-OxF0RGinstall:
    No such file or directory

    The path backslashes are stripped, matching this issue.

  5. Tsuki-hash commented on Sep 25, 2026

    @Tsuki-hash

    @EloyRD that is a second independent reproduction, and the stripped-backslash form matches the WSL entry point rather than Git Bash. One detail worth recording for triage: opencode update is an alias of the upgrade command (packages/cli/src/commands/commands.ts:62), and the alias routes into the same Updater.upgrade, so both spellings and the TUI "update available" dialog all go through the single bash spawn in packages/cli/src/services/updater.ts that #51038 changes. Your install path (%USERPROFILE%\.opencode\bin\opencode.exe) is the curl layout, which is the method that spawns the installer script.

  6. Tsuki-hash commented on Sep 26, 2026

    @Tsuki-hash

    @yveming bullet 2 says the backslash consumption is "independent of which bash binary is ultimately invoked". Worth settling, because if true then #51038 fixes only half of it. Three measurements of the same single argv element (C:\Users\<name>\.cache\opencode\update-XXXX\install, spawned directly, no shell in between, no quoting in play):

    bash that answered result
    Git Bash (...\Git\usr\bin\bash.exe and ...\Git\bin\bash.exe) exit 0, script runs; backslashes accepted
    WSL via C:\Windows\System32\bash.exe exit 127, /bin/bash: C:Users\<name>...install: No such file or directory
    WSL via ...\WindowsApps\bash.exe (@jpgomezv's machine) exit 127, /bin/bash: C:\Users\<name>\...install: No such file or directory

    So the two wordings are produced by two different WSL entry points, not by a quoting layer sitting before bash, and in both WSL cases the script file is present (@jpgomezv verified before and after). A Windows path with backslashes is not itself a failure - which binary receives it is the whole difference. That is why #51038 selects the shell by asking each candidate its own uname -s and deliberately does no path rewriting: quoting the argument would still let WSL bash run the script, and then install reads uname -s as Linux and drops a Linux binary into the Windows install directory.

    Your bullet 1 matches the shipped shape we read in packages/cli/src/services/updater.ts too, so the spawn being fixed is the same one.

  7. angeloruggieridj commented on Sep 29, 2026

    @angeloruggieridj

    Independent reproduction, plus confirmation that the fix's "Git installed but absent from PATH" case resolves the failure.

    Environment: Windows 11 Pro build 26200 (Windows_NT 10.0.26200, x64), opencode v2.0.16 curl install at %USERPROFILE%\.opencode\bin\opencode.exe, launched from cmd.exe.

    This machine has both WSL entry points present, and Git for Windows installed but its bin absent from PATH (only Git\cmd is), so bash never reaches Git Bash:

    > where bash
    C:\Windows\System32\bash.exe
    C:\Users\aruggieri\AppData\Local\Microsoft\WindowsApps\bash.exe
    
    C:\Program Files\Git\bin\bash.exe   (exists, not on PATH)
    

    Updater spawn from ~/.local/share/opencode/log/opencode.log:

    message="OpenCode update available" current=2.0.16 latest=2.0.19
    command=curl args=["-fsSL","-o","C:\\Users\\aruggieri\\.cache\\opencode\\update-UMO6ll\\install","https://opencode.ai/v2/install"]
    command=bash args=["C:\\Users\\aruggieri\\.cache\\opencode\\update-UMO6ll\\install","--version","2.0.19","--no-modify-path"]
    

    Result: /bin/bash: C:Usersaruggieri.cacheopencodeupdate-UMO6llinstall: No such file or directory — the stripped-backslash form, matching the System32 stub in your table.

    Confirming the fix's key case: the installer script handed to C:\Program Files\Git\bin\bash.exe with the native C:\... path runs correctly, no path conversion needed:

    $ "C:\Program Files\Git\bin\bash.exe" -lc 'uname -s; uname -m'
    MINGW64_NT-10.0-26200
    x86_64
    
    $ "C:\Program Files\Git\bin\bash.exe" -lc 'curl -fsSL https://opencode.ai/v2/install | bash -s -- --version 0.0.0-does-not-exist --no-modify-path'
    Error: Version 0.0.0-does-not-exist is not available for windows-x64
    

    So recovering Git Bash from %ProgramFiles%\Git\bin selects the right platform, and @opencode/[email protected] is published (registry returns 200).

    One gap the shell fix alone won't close (filing separately): on Windows the destination opencode.exe is locked while opencode runs, so even a correct bash cannot replace it. While the TUI and the long-lived background service were up, [System.IO.File]::Open($exe,'Open','ReadWrite','None') failed with "being used by another process". Immaterial to the shell-selection fix, but it shares this code path.

  8. Tsuki-hash commented on Sep 29, 2026

    @Tsuki-hash

    @angeloruggieridj thanks for the third independent reproduction — and for testing the exact edge case. "Git for Windows installed but Git\bin not on PATH" is precisely what #51038 covers: the candidate list includes %ProgramFiles%\Git\bin\bash.exe even when it isn't on PATH, and picks the first candidate whose uname -s reports MINGW/MSYS/CYGWIN — matching your measurement that the native C:\... path works once the right bash runs it.

    Agreed on the write lock: it shares the code path but is a separate failure from shell selection, so tracking it separately in #52025 is the right call.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions