Skip to content

shell: every spawn fails with ERROR_PATH_NOT_FOUND on Windows (path specified) until shell is pinned to an absolute path #55733

Open

Description

@hrabbach

Summary

On Windows, every shell tool call in a session fails instantly with the raw Windows error The system cannot find the path specified. (exit 1) — before the command runs. Pinning an absolute shell path in config ("shell": "C:\\Program Files\\PowerShell\\7\\pwsh.exe") immediately fixes it via hot-reload, which points at shell resolution selecting a non-functional executable (cached at process start) rather than a cwd/spawn problem.

Environment

  • opencode version: v2.0.29
  • OS: Windows 11 (10.0.26300.9550, win32 x64)
  • Terminal: TUI running in Windows Terminal (WT_SESSION set; TERM=xterm-256color; OPENCODE=1)
  • Shell: C:\windows\system32\cmd.exe (ambient at session start); $env:SHELL unset; pwsh 7.6.6 and Git bash.exe both installed and present on disk
  • Install/channel: release
  • Active plugins: none configured in opencode.json

Reproduction

  1. Start the OpenCode background service on Windows (TUI client in Windows Terminal). No shell key in config.
  2. In a session, invoke the shell tool with any command (e.g. echo hello), with or without an explicit workdir pointing at an existing directory.
  3. Every call returns The system cannot find the path specified. + Exited with code 1, instantly.
  4. Add "shell": "C:\\Program Files\\PowerShell\\7\\pwsh.exe" to the global opencode.json. After hot-reload, the same calls succeed. Removing the key reverts to the failure after the next hot-reload (verified in the same running process).

Expected Behavior

The shell tool resolves a valid, working shell executable and runs the command; if resolution fails it should re-resolve (or surface a clear error naming the resolved path), not emit the raw OS error on every call.

Actual Behavior

Every spawn fails with:

The system cannot find the path specified.

Exited with code 1

Evidence that resolution — not the working directory — is at fault:

  • pwsh.exe (C:\Program Files\PowerShell\7\pwsh.exe) and bash.exe both exist on disk and execute normally; no shell override was configured at the time of failure.
  • The failure began ~1 hour after the service was started and nothing about the installed shells changed on the machine in that window — so this is not a "path existed at startup and was later removed" scenario; the resolver picked a non-working executable from the start.
  • The workdir parameter (set to existing directories) did not change the outcome.
  • session_move of the affected session to an existing directory did not help.
  • Setting an absolute shell path in config fixed all calls immediately after config hot-reload — no service restart needed — and removing it re-broke them, showing the configured path bypasses a process-lifetime resolution cache.

Additional Context

  • Possibly related: Windows: pwsh installed as a Store app-execution alias is not resolved, so the shell falls back to Windows PowerShell 5.1 #55255 (shell resolution misses Store app-execution aliases and silently falls back — the reporter's pwsh is a regular install at C:\Program Files\PowerShell\7, but the same silent-fallback code path may be involved), cli: no way to refresh environment variables without restarting #48919 (environment changes aren't picked up without restarting the service).
  • The error text matches what cmd.exe prints when the executable it was asked to run doesn't exist, consistent with the spawn wrapper invoking a resolved-but-nonfunctional shell path.
  • Suggested hardening: (1) validate the resolved shell executable exists and actually spawns before caching it, and re-resolve on failure; (2) when resolution yields no usable executable, emit a warning naming what was tried instead of falling back silently; (3) include the resolved shell path in the shell tool error output.
  • Workaround that fully restores function: set "shell" to an absolute path in global config.
  • Frequency: observed on v2.0.29 from the first shell call of a session (~1 hour into a fresh service start); persists for the life of the service process until a shell is configured. Not yet reproduced under OPENCODE_LOG_LEVEL=DEBUG to identify the exact resolved value.

Activity

  1. opencode-agent commented on Oct 15, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report. I couldn't try to reproduce this, because it only happens on Windows (it showed up in the desktop app's background service after a few days) and our test environment is Linux, so it will need a maintainer with a Windows machine.

    Some notes from reading the code, for whoever picks it up. None of this is confirmed:

    • When no shell is configured, the resolved shell is cached for the whole server process and never refreshed. A configured shell skips that cache, which would explain why your workaround works after a hot-reload without a restart.
    • If no shell is found, the code silently falls back to cmd.exe (via COMSPEC). The error text The system cannot find the path specified. looks like cmd.exe's own message rather than a failure to start the process. So cmd.exe may have been picked and then hit a path that didn't exist.

    If it happens again before you set the workaround, any log lines from that session about the shell or the instance would help.

    Related:

    Until then, setting "shell" to an absolute path in your global config works around it, as you found.

  2. hrabbach commented on Oct 15, 2026

    @hrabbach
    Author

    Correction to my previous comment — please disregard its speculation.

    Corrected facts (verified with the reporter):

    • The background service had been running for only ~1 hour when the failure appeared. Nothing on this machine's shells changed in that window — both pwsh 7.6.6 (C:\Program Files\PowerShell\7\pwsh.exe) and Git bash.exe exist and execute fine. So the "cached path stopped existing / Git moved" narrative was unfounded, and the earlier shell activity seen in the log belonged to a previous service run, not this one.
    • What is verified: with no shell configured, every shell tool call fails with The system cannot find the path specified. + exit 1 (including echo hello, with or without a valid workdir). Toggling "shell": "C:\\Program Files\\PowerShell\\7\\pwsh.exe" in global config off/on via config hot-reload toggles the failure in the same running process — broken without the key, working with it.
    • So the accurate statement is: on this machine, startup shell resolution selects an executable that does not work (spawn via the cmd wrapper fails), and that choice is cached for the process lifetime; an absolute configured shell bypasses the cache entirely. Both plausible pwsh/bash candidates exist on disk, so whatever the resolver picked, it wasn't a usable one — consistent with the silent-fallback weakness in shell/select.ts (cf. Windows: pwsh installed as a Store app-execution alias is not resolved, so the shell falls back to Windows PowerShell 5.1 #55255), but the exact resolved value is unconfirmed.

    Next step if useful: run a fresh opencode --standalone (or restarted service) with OPENCODE_LOG_LEVEL=DEBUG on this machine to capture exactly what the resolver selects at startup. Happy to do that on request.

    And to restate the earlier point with the corrected timeline: since the bad resolution is cached at process start, the practical impact is that a machine where resolution goes wrong gets a completely broken shell tool — for every project, every command — with an error message that names no path and hints at no config remedy.

  3. hrabbach commented on Oct 15, 2026

    @hrabbach
    Author

    Update after a debug capture session — the plot thickens in a useful way.

    What we did: killed the affected service (opencode.exe serve --service, started ~1 h before the failure) and started a fresh one with OPENCODE_LOG_LEVEL=DEBUG / --print-logs, with no shell configured (the config workaround removed).

    Result: on the fresh service, the shell tool works. The debug log shows serve-mode resolution picking the exact expected binary:

    message="spawning process" command="C:\Program Files\PowerShell\7\pwsh.EXE" args="[\"-NoLogo\",\"-NoProfile\",\"-NonInteractive\",\"-Command\",...]" cwd="..." role=server
    

    Earlier, a fresh opencode run --standalone (no shell configured, env inherited from the service) resolved the same pwsh.EXE and worked as well.

    Conclusion so far:

    • Shell resolution logic works correctly on this machine in both run and serve --service modes, in the service's own environment.
    • The failure was bound to the previous service process's in-memory cached resolution, which had become invalid at some point during that process's ~1 h lifetime and poisoned every subsequent spawn (config hot-reload toggle proved the cache is read per spawn and bypassed by a configured shell).
    • The trigger that poisons the cache is still unknown — it is not "path existed at startup and was later deleted" (nothing changed on the machine's shells), and it does not reproduce on a fresh service so far.

    Practical notes for whoever picks this up:

    • Impact when it hits: every shell tool call in every project fails with a raw OS error (The system cannot find the path specified., exit 1) for the life of the service process — severe but recoverable.
    • Recovery that worked instantly without killing the session: set "shell" to an absolute path in global config (hot-reload bypasses the cache). A service restart also clears it.
    • Suggested hardening (restated with the new evidence): validate the cached shell executable exists (and actually spawned) before use; on spawn failure, re-resolve once and warn with the resolved path instead of failing every call silently. The error signature (cmd.exe's own "path not found" text on every command, even echo hello) suggests the cached value was a path that did not exist at spawn time.

    Reporter's config is back to no shell key and shell is working; will update if the failure reappears and a capture of the poisoned state becomes possible.

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