[BUG] claudeCode.usePythonEnvironment silently no-ops when claudeCode.useTerminal is enabled

Status Open
Reported on v2.1.263
Maintainer reply None cached
Activity 0 comments · opened Sep 8, 2026

Summary

claudeCode.usePythonEnvironment (default true) is only honored on the SDK spawn path used by the native UI. When claudeCode.useTerminal is true, Claude is launched in a VS Code integrated terminal instead, and that path never consults the setting — so the workspace's Python environment is silently not activated. Nothing surfaces to indicate the setting has been bypassed.

The two settings read as independent, but enabling terminal mode disables Python environment activation as a side effect.

Observed

In a workspace with a .venv and that interpreter selected, running under claudeCode.useTerminal: true with usePythonEnvironment left at its true default:

  • VIRTUAL_ENV is unset in the claude process environment.
  • .venv/bin is absent from PATH.
  • python resolves to my pyenv shim rather than .venv/bin/python.

Inspecting the environment of the claude node process directly (65 variables visible, PATH among them) confirms no VIRTUAL_ENV and no .venv entry. The Python extension is definitely active in that process and has an interpreter selected for the workspace — VSCODE_PYTHON_AUTOACTIVATE_GUARD=1, PYTHONSTARTUP and VSCODE_DEBUGPY_ADAPTER_ENDPOINTS are all present in the same environment — so the documented "Requires the Python extension" dependency is satisfied.

Where it diverges

From extension.js in anthropic.claude-code-2.1.263-darwin-arm64 (minified, lightly trimmed):

The SDK spawn path reads the setting and builds an environment from it:

let {pathToClaudeCodeExecutable: Z, executableArgs: D, env: O} = this.getClaudeBinary(),
    L = P1("usePythonEnvironment") ?? !0,
    M = L ? await Wu$(O) : O;
if (L && M !== O) {
  let j = ["PATH", "VIRTUAL_ENV", "CONDA_PREFIX", "CONDA_DEFAULT_ENV"];
  for (let R of j) if (M[R] !== O[R]) this.output.info(`Python env: ${R}=${M[R]}`)
}
N.pathToClaudeCodeExecutable = Z, N.executableArgs = D, N.env = M;

Wu$ is the helper that performs the activation:

let W = z.executable?.sysPrefix || (z.executable?.uri ? y2.dirname(y2.dirname(z.executable.uri.fsPath)) : void 0);
if (!W) return $;
let K = {...$};
K.VIRTUAL_ENV = W;
let U = process.platform === "win32" ? y2.join(W, "Scripts") : y2.join(W, "bin"),
    G = K.PATH || process.env.PATH || "";
return K.PATH = `${U}${y2.delimiter}${G}`, console.log(`Python environment activated: VIRTUAL_ENV=${W}`), K

The terminal path builds its environment independently, and it contains exactly one variable:

C0.window.createTerminal({
  name: process.env.CLAUDE_CODE_TERMINAL_TITLE || "Claude Code",
  iconPath: ...,
  location: W,
  isTransient: !0,
  env: {NoDefaultCurrentDirectoryInExePath: "1"}
})

No Wu$ call, no usePythonEnvironment lookup, no VIRTUAL_ENV.

Repro

  1. Open a Python workspace containing a .venv and select that interpreter.
  2. Leave claudeCode.usePythonEnvironment at its default (true).
  3. Set claudeCode.useTerminal: true.
  4. Open Claude and inspect the environment — VIRTUAL_ENV is unset and command -v python does not resolve into .venv/bin.
  5. Set claudeCode.useTerminal: false, reload the window, and repeat step 4 — VIRTUAL_ENV is now set to the workspace .venv, and .venv/bin is prepended to PATH.

Both halves are observed rather than inferred. Same workspace, same selected interpreter, usePythonEnvironment left at its default throughout; the only variable changed is useTerminal. Reading the claude process environment directly:

With useTerminal: true — 65 variables visible, no VIRTUAL_ENV, no .venv entry in PATH. Parent process is the integrated terminal's /bin/zsh -il.

With useTerminal: false — 69 variables visible, VIRTUAL_ENV=<workspace>/.venv, and PATH begins <workspace>/.venv/bin:.... Parent process is the extension host, and the executable is resources/native-binary/claude.

That matches the code paths above exactly: the SDK spawn applies Wu$ and the terminal spawn does not.

Expected

Either the terminal path honors usePythonEnvironment and contributes the same VIRTUAL_ENV / PATH that Wu$ produces, or the setting's description notes that it does not apply in terminal mode. Silently no-opping is the bad case: python, pip and pytest invoked from Claude resolve against whatever interpreter happens to be first on PATH, with no indication that the environment the user selected was skipped.

Impact

Low visibility rather than high severity. Anyone driving tooling through explicit paths (.venv/bin/pytest and so on) never notices. Anyone who types a bare python or pytest gets the wrong interpreter silently, which in a project pinned to a specific Python version can produce confusing failures a long way from the cause.

Environment

  • Claude Code CLI 2.1.252, VS Code extension 2.1.263 (darwin-arm64)
  • ms-python.python 2026.4.0
  • macOS 26.6.2 (Apple silicon), zsh
  • claudeCode.useTerminal: true, claudeCode.usePythonEnvironment unset (default true)

Related

Turned up while investigating #80657, where the same useTerminal: true configuration causes the Python extension's terminal.sendText() activation command to land in Claude's prompt input instead of a shell. The two are separate defects with a shared trigger, and together they compound: terminal mode disables the activation mechanism that works, and exposes the one that doesn't. Details and the process-level evidence are in https://github.com/anthropics/claude-code/issues/80657#issuecomment-5586877297.

View original on GitHub ↗