Repository navigation
Description
Description
OpenCode V2 installed at %USERPROFILE%\.opencode\bin\opencode.exe cannot be upgraded from native Windows PowerShell. The documented installer is bash-only, and Windows package managers are not supported. The working update is a manual replace from opencode-windows-x64.zip.
bash.exe on this machine is the WSL stub. The only WSL distro is stopped docker-desktop, so curl -fsSL https://opencode.ai/v2/install | bash is not a native Windows path. The V2 installer only treats MINGW / MSYS / CYGWIN as Windows, then mvs the new binary over the running exe. Windows locks that file, so the replace cannot succeed while OpenCode is running.
This is V2 (https://opencode.ai/v2/install, 2.0.15 to 2.0.18, refs/heads/v2). The open V1 report is the same class of bug, and its fix is still unmerged: #37055, PR #48368.
Workaround that worked: download https://opencode.ai/files/bin/2.0.18/opencode-windows-x64.zip, rename the running exe to opencode.exe.old, copy the new opencode.exe into %USERPROFILE%\.opencode\bin\, then restart OpenCode. A new process then reports opencode v2.0.18.
The V2 docs download links still point at the 2.0.6 zips, not 2.0.18.
Plugins
None
OpenCode version
2.0.15, channel latest. Latest published build is 2.0.18.
Steps to reproduce
- On native Windows PowerShell, with no Linux WSL distro, install V2 so the binary is
%USERPROFILE%\.opencode\bin\opencode.exe. - Run
opencode --version. It prints2.0.15. - Run
opencode upgrade, orcurl -fsSL https://opencode.ai/v2/install | bashfrom PowerShell. - The installer is bash-only. PowerShell
bashis the WSL stub, and the scriptmvs onto the locked exe. - Run
opencode --versionagain. It stays on the old build unless the exe is replaced by hand.
Screenshot and/or share link
No share link. Confirmed latest via https://opencode.ai/update/api/latest/cli/npm (version 2.0.18) and by running the extracted zip binary.
Operating System
Windows 11 (10.0.26200, win32 x64). Shell is Windows PowerShell 5.1. Not WSL.
Terminal
Unavailable. Terminal environment variables are not set.
Activity
github-actions commentedon Sep 28, 2026
This issue might be a duplicate of existing issues. Please check:
opencode upgradefails silently on Windows - binary never replaced #37055: Windows curl upgrade cannot replace the running executable because Windows locks it.loisekk commentedon Oct 11, 2026
Your
.oldworkaround is the correct fix, and the reason it works pins down which part is actually broken.There are two different operations in the installer and only one of them fails against a running executable:
install.sh:343—mv--binaryinstall.sh:354—cpDevice or resource busyMeasured with Git Bash coreutils 8.32 on Windows 11:
cphas to open the destination for writing, and a running image refuses that.mvdoesn't — it's a rename, and Cygwin/MSYS opens withFILE_SHARE_DELETEso the rename is permitted. Same binary, same lock, opposite outcome, purely because of how each opens the file.Which is why rename-to-
.old-then-copy works for you: the rename-aside is the only part that a running image allows, and once the name is vacated thecphas nothing to overwrite. Doing rename-aside first is the fix, not just the workaround.Against the normal download path, though, the lock isn't the obstacle at all:
With the binary running in the install dir the whole time. Same-fs and cross-filesystem both replace it.
So for your setup the blocker is the one you identified —
bash.exeis the WSL stub and there's no usable distro — not themv.curl -fsSL https://opencode.ai/v2/install | bashfrom PowerShell has no native Windows path, becauseinstall.shonly treatsMINGW/MSYS/CYGWINas Windows andmvs onto a locked exe. On a Git-for-Windows install withbashresolving toC:\Program Files\Git\bin\bash.exe,uname -sisMINGW64_NT-10.0-26300and that command works in place.That's the same shell-selection problem #51038 and #52025 are on. Your
OPENCODE_GIT_BASH_PATHcheck ininstallerBash()should cover it — worth confirming it's set for your machine, since with itopencode upgradeshould take the working path rather than needing the manual replace.One more thing on your last line:
2.0.6in the docs while2.0.18is current looks like a real docs bug, separate from this.loisekk commentedon Oct 16, 2026
Your bash diagnosis was right, and I think the locking half of it is wrong. Both are worth separating, because the first one is a real trap and the second sends you down a dead end.
The bash half
On a machine like yours, where
bashresolves toC:\WINDOWS\system32\bash.exe:V1's
upgradeScriptShell()inpackages/opencode/src/installation/index.tswas:It only checks that the command printed something. The stub prints a full GNU bash banner and then exits
-1, so the check passes and the installer gets piped into WSL. It runs inside Linux,$HOME/.opencode/binis a Linux path, and the upgrade writes to a filesystem the Windows install never reads — which is why the UI can reportInstalling OpenCode 1.19.3…while--versionstays put. Ignoring the exit code there is what makes it silent.V2 already closes this.
installerBash()ondevnever returns a barebashon Windows: it honoursOPENCODE_GIT_BASH_PATH, otherwise derives Git Bash fromgit --exec-path, otherwise fails loudly with "Git Bash is required to update OpenCode". On this machine that derivation resolves:So on 2.0.x, either install Git for Windows or set
OPENCODE_GIT_BASH_PATH, and the upgrade runs natively. V1 has no equivalent — I checked,GIT_BASHdoesn't appear anywhere in the V1 tree.The locking half
"Windows locks that file, so the replace cannot succeed while OpenCode is running" — I couldn't reproduce that, and I think the probe in #52025 is what makes it look true:
That's accurate for a native Win32 caller, which doesn't request
FILE_SHARE_DELETE. Butinstall.sh:343ismv, and under MINGW64 that goes through Cygwin, which does request it. Both observations are correct for their own caller.Running the real V1 installer against a sandboxed
HOMEwith the install-dir binary live:Same-fs and cross-filesystem both replace it. The one path that genuinely can't work over a running exe is
cpatinstall.sh:354, used by--binary—cphas to open the destination for writing:That's the shape of your
.oldworkaround, and doing rename-aside first is the correct fix for that path rather than a hack.Caveats
I used
ping.exeas the stand-in running binary, notopencode.exeitself — same Windows semantics, but not the same image. And I ran under Git Bash invoked by absolute path, which is why I could get a clean result where your barebashcould not. I didn't test with a live WSL distro present, so I can't say exactly what the stub does in your docker-desktop case — only that its banner passes V1's check and its exit code is non-zero, which is enough for the bug to happen.The most useful thing here might be surfacing installer stderr on failure. Three separate reports have now misdiagnosed this, and
opencode upgradecurrently discards it.opencodejr commentedon Oct 28, 2026
Thanks for the detailed write-up and the workaround. Here is where each part stands in current v2:
bashstub: fixed in v2.0.23 (fix(cli): run the curl installer with Git Bash on Windows #53029, reported in update: /update fails on Windows when bash resolves to the WSL launcher #52815). On Windows,opencode upgradenow runs the installer with Git Bash instead of whateverbashis first onPATH. It usesOPENCODE_GIT_BASH_PATHif you set it, and otherwise looks for Git Bash itself. If it can't find Git Bash, it stops with "Git Bash is required to update OpenCode" instead of using the WSL stub.opencode.exe: during an upgrade on Windows, the updater keeps a second hard link to the running binary in the cache, so the installer can replace the file while OpenCode is running. This is similar to your rename-aside workaround. cli: Windows curl upgrade cannot replace the running opencode.exe (locked by the TUI and background service) #52025 has more discussion of whether this is enough in every case.latest, not2.0.6.What's left from your report is a way to install and upgrade from native PowerShell on a machine without Git Bash, for example a PowerShell installer. The updater doesn't support that today, so I've marked this as a feature request for the maintainers to consider.
Related issues:
bashupgrade failure (fixed)neriousy commentedon Nov 12, 2026
OpenCode v2 is now available on WinGet. Could you try this from PowerShell? No WSL, Git Bash, or npm needed.
WinGet installs its own copy rather than replacing the exe you extracted. In a new PowerShell window, run
Get-Command opencode -Allto see which copy comes first, thenopencode --version. For future updates, usewinget upgrade --id Anomaly.OpenCode --exact.Could you confirm whether PowerShell picks up the WinGet copy? If it still finds the old exe first, please share the paths from
Get-Command opencode -All.