Skip to content

cli: no way to refresh environment variables without restarting #48919

Open

Description

@XXLxhPLMM

Summary

After modifying system/user environment variables (e.g., adding a new directory to PATH), OpenCode does not detect the changes until a full restart. There is no built-in way to refresh environment variables within a running session.

Environment

  • opencode version: 2.0.3
  • OS: Windows_NT 10.0.26200 (win32 x64)
  • Shell: Git Bash (MINGW64)

Reproduction

  1. Install a CLI tool to a new directory (e.g., D:\Tools\gh\bin\)
  2. Add the directory to the user PATH via system settings
  3. In an active OpenCode session, try to use the tool: gh --version
  4. Command not found — the new PATH is not reflected

Expected Behavior

OpenCode should either:

  • Re-read environment variables on demand (e.g., a refresh-env command or API)
  • Or detect PATH changes and update the shell environment automatically

Actual Behavior

The only workaround is to fully restart OpenCode. All running sessions, context, and state are lost.

Additional Context

This affects any workflow where tools are installed or environment variables are modified mid-session. Common scenarios:

  • Installing new CLI tools (gh, docker, etc.) via package managers
  • Modifying proxy settings (HTTP_PROXY, NO_PROXY)
  • Switching Node.js versions via nvm
  • Updating API keys or tokens stored in env vars

Activity

  1. VishvakR commented on Sep 14, 2026

    @VishvakR

    this isn't really opencode holding onto old values, a process gets its env when it starts and windows doesn't push PATH changes into processes that are already running. same reason the Git Bash window you had open won't see the new PATH either.

    on 2.0.3 the cli snapshots its env at launch (Env.session() in packages/cli/src/env.ts) and the tui sends it to the session through session.environment when you open it, and the shell tool spawns with that. so you shouldn't lose anything by restarting, open a fresh terminal and run opencode --continue (or pick the session again) and the new PATH goes into that session. history is stored, it's not dropped on restart.

    an actual in-app refresh would mean reading the user/system env out of the registry on windows and deciding how it merges with what the session already has, which seems like a design call for the team more than a quick PR.

  2. XXLxhPLMM commented on Sep 14, 2026

    @XXLxhPLMM
    Author

    To add a practical use case to this discussion:

    I frequently need to configure proxy settings (HTTP_PROXY, HTTPS_PROXY, NO_PROXY, etc.) or other environment variables that should only apply to OpenCode — not to my entire system. For example:

    • Proxy configuration for API requests (providers, MCP servers)
    • Custom PATH entries for project-specific CLI tools
    • API keys or tokens scoped to a single project

    What I think would help:

    1. Config-level environment overrides — Allow setting environment variables in opencode.jsonc (global or project-level) that are applied to all sessions spawned by OpenCode. Something like:

      {
        "environment": {
          "HTTP_PROXY": "http://127.0.0.1:7890",
          "PATH": "${PATH};D:\\Tools\\gh\\bin"
        }
      }
    2. TUI/Desktop env editor — A built-in UI (accessible via a shortcut or command) to view and edit environment variables for the current session or project, without needing to restart OpenCode.

    3. Per-session env refresh — When starting a new session (not restarting OpenCode), re-read the current system environment and merge it with any config-level overrides. This way, even if the running OpenCode process has stale env vars, a fresh session picks up the latest.

    This would solve the proxy use case cleanly: I set HTTP_PROXY in my project's opencode.jsonc, and every session in that project uses it for all outbound requests — regardless of what the host terminal's environment looks like.

  3. XXLxhPLMM commented on Sep 14, 2026

    @XXLxhPLMM
    Author

    Quick follow-up question: if I modify the system environment variables and then run opencode service restart, would the new sessions pick up the updated environment without losing existing session history? Or does the service also snapshot the env at its own launch time?

  4. VishvakR commented on Sep 14, 2026

    @VishvakR

    put up a draft for this anyway, it adds a windows-only /refresh-env command in the tui that reads the Machine and User variables from the registry through powershell and pushes them into the open session.

    it only adds things: new PATH entries get appended and variables the session doesn't have get added, nothing existing gets replaced. that keeps the PATH entries Git Bash sets up for itself, but it also means an edited value like HTTP_PROXY won't change until restart. if overwriting non-PATH vars is preferred i can switch it. haven't been able to run it on windows myself, so it'd be good if someone could try it there.

  5. VishvakR commented on Sep 14, 2026

    @VishvakR

    yeah the service snapshots its env too. it gets spawned as a detached child of whichever opencode process starts it, with that process's env plus anything you set through opencode service set env (spawnServiceContender in packages/client/src/service-contender.ts). so opencode service restart run from a terminal you opened after changing the vars gives the service the new env, and history is fine since sessions are stored, not held by the service process. the cli restart does close persistent terminals though. the tui's /restart won't help here, that respawns the service from the tui process which still has the old env.

    the catch is the shell tool. once the tui has pushed a session env it spawns with sessionEnvironment ?? process.env (packages/core/src/shell.ts), and an open tui re-pushes its own launch snapshot as soon as it reconnects. so a service restart updates what the service does itself, provider requests and mcp servers, but bash commands in a tui that was already open still get the old PATH. for those you need to reopen the tui from a new terminal, opencode --continue.

    for the proxy case opencode service set env HTTP_PROXY http://127.0.0.1:7890 already exists and sticks across restarts, same caveat that it applies to the service process and not to shell commands in a tui session.

  6. opencodejr commented on Oct 28, 2026

    @opencodejr
    Contributor

    Thanks for the clear write-up! You're asking for a way to pick up changed environment variables (like a new PATH entry) in a running OpenCode session, without a restart. Right now OpenCode reads its environment once at startup, so this works as designed today. It's a reasonable improvement, though, and maintainers will consider it.

    In the meantime, a plugin can cover the most common case: commands run by the shell tool (e.g. gh --version after editing PATH in System Settings). The plugin below re-reads your user and system environment from the Windows registry before every shell command. New Path entries go first, and any entries OpenCode already had are kept.

    Save it as ~/.config/opencode/plugins/refresh-env.ts (or .opencode/plugins/refresh-env.ts in a project), then restart OpenCode once:

    import { execFileSync } from "node:child_process"
    
    // Re-reads Windows user + system environment variables from the registry
    // before every shell command, so changes made in System Settings (e.g. a new
    // PATH entry) apply without restarting OpenCode.
    const KEYS = [
      "HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment",
      "HKCU\\Environment",
    ]
    
    function read(key: string) {
      const vars: { name: string; type: string; value: string }[] = []
      try {
        const out = execFileSync("reg", ["query", key], { encoding: "utf8", windowsHide: true })
        for (const line of out.split(/\r?\n/)) {
          const m = line.match(/^\s{4}(\S.*?)\s{4}(REG_SZ|REG_EXPAND_SZ)\s{4}(.*)$/)
          if (m) vars.push({ name: m[1], type: m[2], value: m[3] })
        }
      } catch {}
      return vars
    }
    
    function keyOf(env: Record<string, string | undefined>, name: string) {
      return Object.keys(env).find((k) => k.toLowerCase() === name.toLowerCase()) ?? name
    }
    
    function expand(value: string, env: Record<string, string | undefined>) {
      return value.replace(/%([^%]+)%/g, (all, name: string) => env[keyOf(env, name)] ?? all)
    }
    
    export default {
      id: "refresh-env",
      setup: async (ctx: any) => {
        if (process.platform !== "win32") return
        await ctx.shell.hook("create.before", (input: { env: Record<string, string | undefined> }) => {
          const env = input.env
          const paths: string[] = []
          for (const key of KEYS) {
            for (const v of read(key)) {
              const value = v.type === "REG_EXPAND_SZ" ? expand(v.value, env) : v.value
              if (v.name.toLowerCase() === "path") paths.push(...value.split(";"))
              else env[keyOf(env, v.name)] = value
            }
          }
          if (paths.length === 0) return
          // Registry PATH first (system, then user), then keep any extra entries already present.
          const pathKey = keyOf(env, "Path")
          const merged: string[] = []
          for (const p of [...paths, ...(env[pathKey] ?? "").split(";")]) {
            if (p && !merged.some((m) => m.toLowerCase() === p.toLowerCase())) merged.push(p)
          }
          env[pathKey] = merged.join(";")
        })
      },
    }

    What this does and doesn't cover:

    • It only changes the environment of commands the agent runs through the shell tool.
    • It doesn't change OpenCode's own process. Provider API keys or HTTP_PROXY used for OpenCode's own requests still need a restart. Covering those would need a new API in OpenCode itself.
    • For nvm-style switches made inside a shell, rather than in the registry, the registry has nothing new to read. Those still need the tool's own setup or a restart.
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