Repository navigation
Shell sessions losing $PATH value from parent shell #4968
Description
Issue: Shell Sessions Losing $PATH Value
Summary
Shell commands executed via the Bash tool receive a minimal system PATH instead of inheriting the full parent shell environment. This causes commands to fail when they depend on tools installed in custom paths (homebrew, nvm, conda, etc.).
Reproduction
Current Behavior
# Parent shell PATH (current Claude Code session)
$ echo $PATH
/Users/optron/.opencode/bin:/Users/optron/.antigravity/antigravity/bin:/Users/optron/.npm-global/bin:/Users/optron/.config/iterm2:/Users/optron/.bun/bin:...
# (40+ paths)
# OpenCode spawned shell PATH
$ opencode run --model openrouter/qwen/qwen3-coder:free "Show me your \$PATH value by running echo \$PATH"
/Users/optron/.local/bin:/Users/optron/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
# (Only 9 minimal system paths)Missing Paths
When OpenCode spawns shell sessions, the following paths are lost:
~/.opencode/bin~/.bun/bin~/.npm-global/bin- Node version managers (nvm, fnm)
- Language managers (conda, rbenv, pyenv)
- Development tools (flutter, android-sdk, docker)
- Custom user paths from
.zshrc/.bashrc
Root Cause
Location: packages/opencode/src/tool/bash.ts:216-224
const proc = spawn(params.command, {
shell,
cwd: Instance.directory,
env: {
...process.env, // This spreads the env, but...
},
stdio: ["ignore", "pipe", "pipe"],
detached: process.platform !== "win32",
})Problem: While process.env is spread correctly, spawn() with shell: "/bin/zsh" (or bash) creates a non-interactive, non-login shell that doesn't source initialization files (.zshrc, .bashrc, .zprofile).
This means:
- Shell RC files are NOT sourced
- Custom PATH modifications in RC files are NOT applied
- Only the minimal system PATH is used
Impact
Commands that depend on tools in custom paths will fail:
node,npm,pnpm(if installed via nvm/volta)python(if using conda/pyenv)docker(if installed via Docker Desktop)flutter,dart(custom SDK paths)- Any homebrew-installed tools
- Project-specific toolchains
Proposed Solutions
Option 1: Launch Interactive Shell (Recommended)
Modify the command to source RC files:
const shellCommand = (() => {
if (typeof shell === 'string' && shell.includes('zsh')) {
return `/bin/zsh -i -c ${JSON.stringify(params.command)}`
}
if (typeof shell === 'string' && shell.includes('bash')) {
return `/bin/bash -i -c ${JSON.stringify(params.command)}`
}
return params.command
})()
const proc = spawn(shellCommand, {
shell: true, // Let system handle shell selection
cwd: Instance.directory,
env: {
...process.env,
},
stdio: ["ignore", "pipe", "pipe"],
detached: process.platform !== "win32",
})Pros: Full PATH inheritance, matches user's shell environment
Cons: Slightly slower startup (RC file sourcing), potential side effects from RC files
Option 2: Explicit PATH Preservation
Explicitly preserve PATH from parent process:
const proc = spawn(params.command, {
shell,
cwd: Instance.directory,
env: {
...process.env,
PATH: process.env.PATH, // Explicitly preserve PATH
},
stdio: ["ignore", "pipe", "pipe"],
detached: process.platform !== "win32",
})Pros: Simple, no RC file sourcing overhead
Cons: Misses other env vars set in RC files (JAVA_HOME, etc.)
Option 3: Login Shell Flag
Use login shell flag:
const shellPath = typeof shell === 'string' ? shell : '/bin/bash'
const proc = spawn(params.command, {
shell: `${shellPath} -l`, // Login shell
cwd: Instance.directory,
env: {
...process.env,
},
stdio: ["ignore", "pipe", "pipe"],
detached: process.platform !== "win32",
})Pros: Sources login profile files
Cons: May not source all interactive RC settings
Testing
To verify fix:
# Test 1: PATH inheritance
opencode run "echo \$PATH" | wc -l
# Should show many paths, not just system defaults
# Test 2: Custom tools available
opencode run "which node && which docker && which bun"
# Should find tools in custom paths
# Test 3: Environment variables
opencode run "env | grep -E '(NVM_DIR|CONDA_|HOMEBREW_)'"
# Should show custom env vars from RC filesAdditional Context
- Platform: macOS (Darwin 25.1.0)
- Shell: zsh (default on macOS)
- OpenCode Version: Latest dev branch
- Related Code:
packages/opencode/src/tool/bash.ts
References
- Node.js spawn documentation: https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
- Shell invocation modes: https://www.gnu.org/software/bash/manual/html_node/Invoking-Bash.html
Activity
github-actions commentedon Dec 2, 2025
Testing comment creation
github-actions commentedon Dec 2, 2025
Duplicate Search Complete
I've reviewed the repository's open issues and found NO clear duplicates for this issue.
Related Issue Found:
This issue appears to be unique in addressing the specific problem of shell sessions spawned by OpenCode not inheriting the full PATH and custom environment variables from the parent shell environment due to non-interactive shell spawning.
Note: Removed test comment. This is the actual duplicate search result.
gkzhb commentedon Jan 10, 2026
Any progress on this issue? I'm facing the same problem with fish shell on Mac OS.
Many cli tools installed from homebrew cannot be called directly with shell tool.
Want to know how this
PATHis set and any way to configure this PATH to temporarily fix this before opencode resovles this issue.Edited:
After some experiments, I noticed that the
bashtool actually launches zsh, even though theSHELLenv var reports fish (the source code explicitly disables fish).Under the bash tool, zsh skips the user’s
.zshrc(not taking effects for me) but still sources/etc/zshenv, so I can inject the PATH there or other files sourced by it.andrewfenn commentedon Jan 23, 2026
I can confirm this bug, hope it gets fixed soon.
gotgenes commentedon Mar 4, 2026
Confirmed workaround for mise / version managers on macOS
I hit this exact issue with mise (a polyglot version manager) —
node,pnpm, and other mise-managed tools weren't on PATH in the bash tool.The root cause is what this issue describes:
bash.tsusesspawn(command, {shell})which Node.js translates tozsh -c <command>— a non-interactive, non-login shell that only sources.zshenv.Workaround: activate mise in
~/.config/zsh/.zshenv(or wherever your.zshenvlives) so it runs in all shell contexts, not just interactive ones:The
unsetis important: if OpenCode inherits stale mise state vars from the parent shell, mise may skip re-activation thinking it's already done. Clearing them first ensures a clean activation.Interesting asymmetry in OpenCode itself
bash.ts(bash tool) →spawn(cmd, {shell: "/bin/zsh"})→zsh -c— non-login, no initprompt.ts(/shellinteractive command) →spawn(zsh, ["-c", "-l", "<init+cmd>"])→ login shell with.zshenv+.zshrcexplicitly sourcedThese are intentionally different (bash tool is performance-oriented), but the gap means the bash tool and
/shellcommand behave very differently for users with version managers.The
.zshenvworkaround bridges the gap without needing OpenCode changes — though a proper fix inbash.ts(login shell or explicit env capture at startup) would be cleaner.github-actions commentedon May 4, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.
hanfengcan commentedon Jun 16, 2026
Any progress on this issue?