Skip to content

Shell sessions losing $PATH value from parent shell #4968

Closed as not planned
Closed as not planned

Description

@Walid-Azur

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 files

Additional 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

Activity

  1. github-actions commented on Dec 2, 2025

    @github-actions
    Contributor

    Testing comment creation

  2. github-actions commented on Dec 2, 2025

    @github-actions
    Contributor

    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.

  3. self-assigned this
    on Dec 2, 2025
  4. gkzhb commented on Jan 10, 2026

    @gkzhb

    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.

    Image Quite weird there's nix paths but without others like hombrew.

    Want to know how this PATH is 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 bash tool actually launches zsh, even though the SHELL env 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.

  5. andrewfenn commented on Jan 23, 2026

    @andrewfenn

    I can confirm this bug, hope it gets fixed soon.

  6. gotgenes commented on Mar 4, 2026

    @gotgenes

    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.ts uses spawn(command, {shell}) which Node.js translates to zsh -c <command> — a non-interactive, non-login shell that only sources .zshenv.

    Workaround: activate mise in ~/.config/zsh/.zshenv (or wherever your .zshenv lives) so it runs in all shell contexts, not just interactive ones:

    # ~/.zshenv (or ~/.config/zsh/.zshenv)
    # Clear any stale mise state from parent shell before re-activating
    unset __MISE_ORIG_PATH MISE_SHELL __MISE_WATCH
    
    eval "$(mise activate zsh)"

    The unset is 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 init

    prompt.ts (/shell interactive command) → spawn(zsh, ["-c", "-l", "<init+cmd>"]) → login shell with .zshenv + .zshrc explicitly sourced

    These are intentionally different (bash tool is performance-oriented), but the gap means the bash tool and /shell command behave very differently for users with version managers.

    The .zshenv workaround bridges the gap without needing OpenCode changes — though a proper fix in bash.ts (login shell or explicit env capture at startup) would be cleaner.

  7. github-actions commented on May 4, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

  8. hanfengcan commented on Jun 16, 2026

    @hanfengcan

    Any progress on this issue?

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