Skip to content

Windows: bash tool ignores shell: pwsh config — always runs PowerShell 5.1 (1.18.15) #41426

Closed as not planned

Description

@Think0508

Environment

  • Windows 11 24H2, opencode 1.18.15 (desktop/TUI)
  • PowerShell 7.6.2 installed via Store (MSIX, WindowsApps app-execution alias)

Problem
The shell config option is documented as "Default shell to use for terminal and bash tool" and the docs state "Compatible shells are also used for agent tool calls". On Windows, however, the built-in bash tool always runs Windows PowerShell 5.1 regardless of the configured shell.

Evidence

  1. Global config ~/.config/opencode/opencode.json contains "shell": "pwsh" (confirmed loaded):
    > opencode debug config
    "shell": "pwsh",
    
  2. pwsh is resolvable and spawnable from Node (same mechanism opencode uses):
    spawnSync('pwsh', ['-NoProfile','-Command','Write-Output hi']) → status 0, stdout "hi"
    
  3. Yet the agent bash tool executes as PowerShell 5.1:
    $PSVersionTable.PSVersion.ToString() → 5.1.26100.8875
    $PSEdition → Desktop
    
  4. The bash tool's generated description literally says "Windows PowerShell (5.1)" — it appears to use a hard-coded Windows default rather than the shell config.

Expected
The bash tool should honor shell: "pwsh" on Windows, matching the documented behavior ("Compatible shells are also used for agent tool calls").

Impact

  • shell config only affects the interactive terminal, not agent tool calls.
  • Windows users cannot make agents use pwsh 7 (modern syntax, UTF-8-no-BOM default), which matters for tools like Set-Content (5.1 corrupts UTF-8 Chinese text) and rg.

Repro steps

  1. Set "shell": "pwsh" in global config
  2. Restart opencode
  3. Invoke the bash tool with $PSVersionTable.PSVersion.ToString()
  4. Observe 5.1.26100.8875 / Desktop

Workaround (agent-side)
Explicitly invoke pwsh per command: pwsh -NoProfile -Command '...' (single-quote the inner command to avoid outer-shell variable expansion).

Activity

  1. github-actions commented on Aug 9, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Please review those issues and consider adding your details there if they cover the same problem.

  2. Hotragn commented on Aug 19, 2026

    @Hotragn

    Taking this if nobody's on it. I think this is the root cause behind #30615 and #17372 as well.

    When PowerShell 7 comes from the Store/MSIX package, pwsh on PATH is a Windows app-execution alias — a zero-byte AppExecLink reparse point rather than a real executable. CreateProcess resolves it by name, but stat and PATH scanning do not see it. Measured on Windows 11:

    fs.existsSync(<alias>)      -> false
    fs.statSync(<alias>)        -> undefined
    spawnSync("winget", ...)    -> status 0    (same alias kind, spawns fine)
    

    Shell.resolve() uses which(), which stats candidates, so a Store-installed pwsh resolves to nothing and select() silently falls back to win()[0] — Windows PowerShell 5.1. The configured shell is never reported as missing, which is why this looks like "config ignored".

    Fix I have working: when which() finds nothing on Windows for a known shell name, fall back to where.exe, which does resolve aliases (exit 0 for winget, exit 1 for absent pwsh/zsh), and use the bare name so CreateProcess resolves it. PR shortly.

  3. added a commit that references this issue on Sep 5, 2026
    ab4a761
  4. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

  5. MotionDevLab commented on Oct 13, 2026

    @MotionDevLab

    Verifying on opencode 1.19.0 / Windows 11 — still reproducible, with one additional data point beyond the alias theory above.

    Environment

    • pwsh --version = 7.6.6, but it exists only as a Store app-execution alias (AppData\Local\Microsoft\WindowsApps\pwsh.exe); Test-Path 'C:\Program Files\PowerShell\7\pwsh.exe' = False. So this machine matches the alias scenario exactly.
    • $env:SHELL is empty.
    • Global config has "shell": "pwsh".

    Test 1 — config shell ignored by bash tool: the bash tool reports $PSVersionTable.PSVersion = 5.1.26100.9444 despite the config. A chained command fails with ParserError ... The token '&&' is not a valid statement separator (PowerShell 5.1 grammar). Consistent with the which()-misses-alias → silent fallback to 5.1 theory.

    Test 2 — $SHELL appears unconsulted entirely (new): launched a nested server via opencode run with SHELL forced in the child environment, then ran the bash tool inside it:

    • SHELL set to the Store-alias pwsh path → inner bash still 5.1.
    • SHELL set to C:\Windows\System32\cmd.exe, command ver → returned a PowerShell CommandNotFoundException, not cmd.exe output. If the resolve chain consulted $SHELL at all, ver would have executed under cmd.

    So on 1.19.0 the bash tool reaches 5.1 through both doors: the configured shell fails alias resolution (this machine), and $SHELL is never read. The v1 resolve chain seems to be config → silent fallback with no $SHELL step (cf. unmerged PR #9724 which proposed config > plugin > $SHELL > fallback).

    Can't verify 2.0 from here (pinned on 1.19.0) — leaving this note so whoever re-verifies has the $SHELL data point. Workarounds in use: semicolon separators, pwsh -NoProfile -Command prefixes, and PTY terminal sessions.

  6. Hotragn commented on Oct 13, 2026

    @Hotragn

    Thanks @MotionDevLab, this is really useful. Your environment (Store-only pwsh 7.6.6, nothing in Program Files\PowerShell\7) is exactly the case I was trying to pin down.

    #48968 ports the alias fix to 2.0. On current v2, findExecutable in packages/core/src/shell/select.ts still uses only which(), so it misses the zero-byte AppExecLink in WindowsApps. select() then silently falls through to win(), which picks Windows PowerShell 5.1. The PR adds a where.exe fallback to resolve the alias.

    On $SHELL: 2.0 handles this differently from what you saw on 1.19.0. resolve() checks the config shell first and then environment(), which reads process.env.SHELL, so 2.0 does consult $SHELL when no shell is configured. #48968 doesn't change that order. If $SHELL still seems to be ignored on 2.0, I think it should be its own issue.

    If you get a chance to try 2.0 (opencode upgrade --major), could you run the same two tests? It would confirm whether the alias fallback is the only problem left.

    @thdxr, since this was closed as a v1 issue but I've confirmed the root cause in the v2 source, would you rather I reopen this or keep the fresh 2.0 issue I filed (#55255)?

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

Metadata

Metadata

Assignees

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