claude-code - 💡(How to fix) Fix [BUG] Windows + Git Bash: tengu_cobalt_ridge experiment silently force-enables the PowerShell tool & "Shell: PowerShell", causing wrong-shell retries (token waste)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

On a Windows system whose active/launching shell is Git Bash, Claude Code silently enrolled this account into the tengu_cobalt_ridge experiment, which default-enables the native PowerShell tool and injects Shell: PowerShell (use PowerShell syntax …) into the system-prompt environment block. Nothing in the UI signals that a beta feature was turned on.

Because the surrounding toolchain on this machine (hooks, skills, statusline, allow-listed commands) is bash-based, telling the model "you are in PowerShell" produces systematic wrong-syntax tool calls → failures → retries → wasted tokens. This is the core defect: an experiment flipped the execution shell and tool schema out from under a correctly-bash environment, with no notice and no SHELL-awareness.

Root Cause

Because the surrounding toolchain on this machine (hooks, skills, statusline, allow-listed commands) is bash-based, telling the model "you are in PowerShell" produces systematic wrong-syntax tool calls → failures → retries → wasted tokens. This is the core defect: an experiment flipped the execution shell and tool schema out from under a correctly-bash environment, with no notice and no SHELL-awareness.

Fix Action

Fix / Workaround

Workaround (verified on 2.1.158)

Code Example

Shell: PowerShell (use PowerShell syntax — e.g., $null not /dev/null, $env:VAR not $VAR, backtick for line continuation). Bash is also available via the Bash tool for POSIX scripts.

---

function qC(){
  let H = process.env.CLAUDE_CODE_USE_POWERSHELL_TOOL;
  if (platform() !== "windows") return isTruthy(H);
  if (isFalsy(H))         return false;  // explicit 0/false/no/off → bash
  if (isTruthy(H))        return true;   // explicit 1/true/yes/on  → PowerShell
  if (gitBashPath() === null) return true;  // no Git Bash → PowerShell (reasonable)
  return checkGate("tengu_cobalt_ridge", /*default*/ false);  // ← else: experiment decides
}

---

"CLAUDE_CODE_USE_POWERSHELL_TOOL": "false",
"SHELL": "/usr/bin/bash"
RAW_BUFFERClick to expand / collapse

Summary

On a Windows system whose active/launching shell is Git Bash, Claude Code silently enrolled this account into the tengu_cobalt_ridge experiment, which default-enables the native PowerShell tool and injects Shell: PowerShell (use PowerShell syntax …) into the system-prompt environment block. Nothing in the UI signals that a beta feature was turned on.

Because the surrounding toolchain on this machine (hooks, skills, statusline, allow-listed commands) is bash-based, telling the model "you are in PowerShell" produces systematic wrong-syntax tool calls → failures → retries → wasted tokens. This is the core defect: an experiment flipped the execution shell and tool schema out from under a correctly-bash environment, with no notice and no SHELL-awareness.

Version / environment

  • Claude Code: 2.1.158 (native installer build)
  • OS: Windows 11
  • Git Bash: installed and detected — CLAUDE_CODE_GIT_BASH_PATH set to a valid bash.exe
  • Launch shell: Git Bash (process chain: cmd → bash → bash → claude.exe)
  • CLAUDE_CODE_USE_POWERSHELL_TOOL: unset (at time of bug)

What gets injected

The environment block advertised to the model:

Shell: PowerShell (use PowerShell syntax — e.g., $null not /dev/null, $env:VAR not $VAR, backtick for line continuation). Bash is also available via the Bash tool for POSIX scripts.

…even though process.env.SHELL resolves to a bash path and the process was launched from Git Bash. A dedicated PowerShell tool is also registered in the toolset.

Root cause (reconstructed from the shipped 2.1.158 binary)

The shell/tool gate consults the experiment as its fall-through default:

function qC(){
  let H = process.env.CLAUDE_CODE_USE_POWERSHELL_TOOL;
  if (platform() !== "windows") return isTruthy(H);
  if (isFalsy(H))         return false;  // explicit 0/false/no/off → bash
  if (isTruthy(H))        return true;   // explicit 1/true/yes/on  → PowerShell
  if (gitBashPath() === null) return true;  // no Git Bash → PowerShell (reasonable)
  return checkGate("tengu_cobalt_ridge", /*default*/ false);  // ← else: experiment decides
}

Both the env-block string (qC()"Shell: PowerShell …") and the tool registration (qC() ? [PowerShellTool] : []) key off this single function. With Git Bash present and CLAUDE_CODE_USE_POWERSHELL_TOOL unset, the decision is delegated entirely to the tengu_cobalt_ridge gate, which returned true for this account.

Two design problems:

  1. The gate never consults process.env.SHELL or the launching shell. A user actively running in Git Bash (with SHELL pointing at bash) is still flipped to PowerShell. The only "is the user a bash user?" signal it uses is the binary "is Git Bash absent?" — which is the wrong question.
  2. Silent enablement. An experiment that changes the execution shell and the tool schema ships with no one-time notice and no surfaced opt-out, so the user only discovers it by debugging wasted-token retry loops.

Expected

  • When Git Bash is installed/detected and SHELL / the launching shell indicates a POSIX shell, the default should remain the Bash tool and Shell: bash. The heuristic should weigh SHELL, not just "is Git Bash missing?"
  • An experiment that flips the execution shell / tool schema should not enable silently — surface a one-time notice, or keep it behind the documented opt-in.

Actual

Silent default-enable via the tengu_cobalt_ridge gate; Shell: PowerShell advertised on a bash system; recurring wrong-shell tool calls and retries that burn tokens.

Impact

  • Wasted tokens / iterations from wrong-syntax → retry loops.
  • Cross-shell foot-guns for bash-based hooks and skills (cf. #59225).

Workaround (verified on 2.1.158)

In settings.jsonenv:

"CLAUDE_CODE_USE_POWERSHELL_TOOL": "false",
"SHELL": "/usr/bin/bash"

false matches the falsy branch → qC() returns false before the experiment is consulted → the PowerShell tool is de-registered and the env block reverts to Shell: bash (use Unix shell syntax …). SHELL makes that line render as bash rather than unknown. Confirmed: after restart the PowerShell tool is gone from the schema and the env block reads Shell: bash.

Related (not duplicates)

  • #59225 — model told Shell=PowerShell while hooks execute under bash. Closest existing issue; this report adds the experiment-gate root cause, the token-cost angle, and the SHELL-blind heuristic.
  • #59588 — docs still describe an opt-in rollout while the behavior is default-enable.
  • #62537 / #63514 — the inverse symptom (PowerShell tool unexpectedly absent under Git Bash). Together with this report they suggest the Git-Bash ↔ PowerShell default heuristic needs a single, coherent, SHELL-aware redesign rather than per-experiment toggling.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix [BUG] Windows + Git Bash: tengu_cobalt_ridge experiment silently force-enables the PowerShell tool & "Shell: PowerShell", causing wrong-shell retries (token waste)