[BUG] PowerShell tool starts PowerShell twice per call (parse, then run): ~0.6 s and ~0.75 s of CPU per command on Windows

Status Open
Reported on v2.1.289
Maintainer reply None cached
Activity 1 comment · opened Oct 10, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet (closest: #64253, about the tengu_cobalt_ridge rollout turning the tool on; it doesn't cover what each call costs)
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

Every PowerShell tool call with a new command starts Windows PowerShell twice:

  1. To parse the command for the permission checks: powershell.exe -NoProfile -NonInteractive -NoLogo -EncodedCommand <parse script>, with the command on stdin and the AST coming back as base64 JSON.
  2. To run it: cmd.exe /d /s /c ""C:\WINDOWS\System32\chcp.com" 65001 >nul & "...\powershell.exe" -NoProfile -NonInteractive -ExecutionPolicy Bypass -Command "$__claudeCodeScript = $env:CLAUDE_CODE_SHELL_LAUNCHER_SCRIPT; ...; Invoke-Expression -Command $__claudeCodeScript" > "<output file>" 2>&1".

I replayed both command lines exactly as Claude Code builds them and accounted the whole process tree of each run through a job object: 25 interleaved runs each, on a Ryzen 9 9950X3D with Windows 11 26200 and Defender real-time protection off.

| Command line | wall p50 | CPU (all processes) |
| --- | --- | --- |
| parse step, Windows PowerShell 5.1 | 320 ms | 414 ms |
| run step, Write-Output hi | 262 ms | 336 ms |
| run step, Get-Content <file> | 288 ms | 363 ms |
| run step without the cmd.exe/chcp.com launcher | 259 ms | 331 ms |
| parse step, PowerShell 7.6.6 | 434 ms | 438 ms |
| run step, PowerShell 7.6.6, Write-Output hi | 373 ms | 381 ms |
| Bash tool line (Git Bash), echo hi | 23–31 ms | 14–28 ms |
| Bash tool line, three pipelines chained with && | 131 ms | 167 ms |

A trivial PowerShell call therefore costs about 0.6 s of wall time and 0.75 s of CPU, 5 to 20 times a Bash call. PowerShell 7, which Claude Code prefers when it finds one, is slower in both steps here, so installing it makes each call cost about 0.8 s. With 32 calls running in parallel, the run step alone manages 53 calls/s at 575 ms each.

In my own sessions from Oct 7 to Oct 9 (Claude Code 2.1.289 to 2.1.296), PowerShell tool calls took 1,345 ms at the median and 609 ms at the 10th percentile, from the tool_use entry to its tool_result. One-program Bash calls took 137 ms and Read took 86 ms.

The launcher costs ~3 ms; the two PowerShell starts are the cost. With tengu_cobalt_ridge on, the system prompt also names PowerShell the primary shell, which steers the model toward the slower tool.

What Should Happen?

At most one PowerShell start per call. For example, the run step could parse the command itself and stop before executing when the checks fail, or one long-lived parser process per session could answer every parse instead of a new PowerShell per command.

Error Messages/Logs

None. The cost is latency and CPU.

Steps to Reproduce

  1. On Windows with Git Bash installed, turn the PowerShell tool on (CLAUDE_CODE_USE_POWERSHELL_TOOL=1, or have the rollout flag).
  2. Ask for one PowerShell call such as Write-Output hi, and list the processes while it runs: Get-CimInstance Win32_Process -Filter "Name='powershell.exe'" | Select-Object ProcessId, CommandLine. The -EncodedCommand parse appears first, then the cmd.exe /d /s /c ... chcp.com 65001 ... powershell.exe ... -Command "$__claudeCodeScript = ..." run.
  3. Time one start the way each step does it: Measure-Command { powershell.exe -NoProfile -NonInteractive -Command exit } takes ~170 ms here before any script runs, and every call pays it twice.

Claude Model

None

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.296 (Claude Code)

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

Warp

Additional Information

I turned the tool off with CLAUDE_CODE_USE_POWERSHELL_TOOL=0 and run powershell.exe from the Bash tool when a task needs it: one start and no parse.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗