[BUG] Windows: the PowerShell tool targets 5.1, so models migrate to Bash and users end up maintaining the workarounds

Status Open
Maintainer reply None cached
Activity 1 comment · opened Aug 4, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

[BUG] Windows: the PowerShell tool targets 5.1, so models migrate to Bash and users end up maintaining the workarounds

Summary

On Windows the PowerShell tool executes through Windows PowerShell 5.1 — the
in-box version, feature-frozen since 2016. Models, trained largely on POSIX and
PowerShell 7 idioms, emit syntax it cannot run. The current mitigation is a block
of caveats in the tool description, which is probabilistic and fails open.

The measurable cost is not the failure count. It is the adaptation: sessions
have migrated ~80% of all shell work to the Bash tool to avoid PowerShell, and six
unrelated projects on this machine have accumulated 35 memory files whose only
purpose is to work around shell behaviour — including four separate projects that
each independently derived "never chain shell commands"
under four different
names.

That is the signature of a defect in the substrate: the same lesson re-learned from
scratch, project after project, because there is nowhere else for it to live.

Related but distinct: #83889 asks for the same pwsh preference for the Desktop
integrated terminal; this issue is about the PowerShell tool the model runs
commands through. #7134 covers Edit/Write forcing UTF-8 onto non-UTF-8 files; the
encoding case below is a different pathway (shell cmdlets, not the file tools).

Environment

  • Windows 11 Pro, build 26100 (24H2); machine built 2024
  • powershell.exe5.1.26100.2161; $PSHOME = C:\Windows\System32\WindowsPowerShell\v1.0;

CLR 4.0.30319; exe ProductVersion 10.0.26100.1 (versioned as Windows — an OS component)

  • pwsh (PowerShell 7) not installed. Nothing has ever prompted for it, and

nothing in Claude Code requires it. On a stock Windows box, 5.1 is "PowerShell".

  • Git Bash present; both a PowerShell tool and a Bash tool are exposed

Evidence

Method: parsed every session transcript under ~/.claude/projects/**/*.jsonl,
pairing each tool_use for the PowerShell/Bash tools with its tool_result.
Period 2026-07-15 → 2026-08-04: 106 transcripts, 6 projects, ~5,828 shell calls.

1. Models are abandoning the PowerShell tool

2026-07:  Bash 3104 (76%)   PowerShell  962 (24%)
2026-08:  Bash 1439 (82%)   PowerShell  323 (18%)

Driven by explicit instructions, in sessions and in project docs, to prefer Bash
"because PowerShell keeps failing". Any low PowerShell failure rate measured
today is a product of that migration, not evidence the tool works.

2. Rework

| Measure | Count |
|---|---|
| Shell calls that errored (any cause) | 308 (5.3%) |
| Commands reissued in changed form after a failure | 190 |
| Shell-syntax / quoting errors | 18 |
| Wrong-shell syntax that failed | 32 |
| Wrong-shell syntax that ran anyway (latent) | 50 |
| git commit calls / errored / redone | 376 / 11 / 5 |

3. Workaround memories: 53 files across 6 projects, 35 shell/tooling

Independently derived, in separate projects, for the same underlying causes:

  • one-command-per-tool-call (×2), avoid-compound-shell-commands,

no-chained-shell-commands, avoid-chained-shell-commands

  • powershell-quote-mangling (×2), commit-messages-via-file, full-paths-in-commands
  • permissions-setup, credits-ocr-permissions-layout, cc-permission-rules-windows
  • user-shell-is-cmd, no-pipe-app-through-head-grep, windows-cmd-and-recoverable-deletes

Failure classes

  1. PowerShell version mismatch&&, ||, ternary, ??, ?.,

-AsHashtable, sane 2>&1, UTF-8 defaults. Valid in 7, fail in 5.1.
Preferring pwsh eliminates this class entirely.

  1. Unix idiomshead, tail, grep, which, touch, 2>/dev/null,

VAR=x cmd. Exist in neither PowerShell. pwsh does not help here;
routing to the Bash tool would.

  1. Quoting / here-strings — the largest observed cause of hard failures,

dominated by git commit -m @'...'@ where the message contains double quotes.

Reproducible examples

A. A Get-Content/Set-Content round-trip silently destroys a UTF-8 source file

$t = Get-Content file.py -Raw                  # decodes as the ANSI codepage
$t = $t.Replace('a','b')
Set-Content file.py -Value $t -Encoding utf8   # writes UTF-8 *with BOM*

Every non-ASCII character (, §, ) becomes mojibake and a BOM is prepended.
python -m py_compile still passes. In my case this corrupted an 11,400-line
tracked source file during three string replacements; the only signal was a
1,642-line git diff on a file touched in three places. It is recoverable
(reverse the cp1252 round trip), but nothing in the tooling flags it, and a less
careful review would have committed it.

B. Captured output is decoded with the wrong encoding — ~142 occurrences

This is not a one-off. Scanning the same 106 transcripts for mojibake signatures
turns up ~142 occurrences of text corrupted in capture, across 5 projects, in
two distinct forms:

| Signature | Count | Example (all from the tools' own console messages) |
|---|---|---|
| Replacement char (U+FFFD) — em-dash destroyed | 123 | IMDb Ingest v027 <?> Self-Test |
| UTF-8 decoded as cp1252 | 21 | multiple strong persistent regions — check candidate boxes |

Distribution: 112 InpaintDelogo, 28 Make VOBs, 7 Credits OCR, 2 Cast Generator,
1 IMDb Ingest.

The capture layer is the variable, not the emitting program. The same
warning line from the same tool on the same day appears both ways in
different tool calls:

WARNING: multiple strong persistent regions — check candidate boxes ...
WARNING: multiple strong persistent regions <?> check candidate boxes ...

Identical source bytes, two different manglings. At least one occurrence is a
file read rather than subprocess output (The spike is milestone one <?>
nothing else gets built first
from a markdown file), so this is not limited to
process stdout.

A previous session's workaround, found in the transcripts, shows the cost
landing on users rather than being reported:

assert t.count('�') > 50, 'em-dashes lost'

Deliberately excluded from that count: mojibake belonging to my own software's
domain — a subtitle OCR path where Subtitle Edit decodes Tesseract output via the
console codepage. That class appears 8 times in the transcripts and every one is
a session reading my documentation about it, not a new incident.

C. Here-string commit messages break on double quotes

git commit -m @'
fix: the notice now reads "1 row" not "1 rows"
'@
# -> error: pathspec 'row' did not match any file(s) known to git

Requested changes

  1. Prefer pwsh when installed for the PowerShell tool, falling back to

powershell.exe. Eliminates failure class 1. (#83889 asks for the same on the
integrated terminal — one discovery path could serve both.)

  1. Surface PowerShell 7's existence on Windows — first-run hint or setup docs.

Nothing today tells users 5.1 is a frozen 2016 feature set.

  1. **Decode captured output as UTF-8, and force UTF-8 in the 5.1 session

bootstrap** — [Console]::OutputEncoding, $OutputEncoding,
$PSDefaultParameterValues['*:Encoding']. Example B is the highest-volume
defect here (~142 occurrences in three weeks) and it silently degrades every
piece of tool output the model reads. (5.1's Set-Content -Encoding utf8
always writes a BOM; only pwsh or [IO.File]::WriteAllText fully fixes
that half.)

  1. Route POSIX-shaped commands to the Bash tool when it is available, rather

than failing in PowerShell — fail closed instead of open.

  1. Guardrail against shell-based rewrites of tracked source files, since

example A passes every syntax check and surfaces only in a diff.

What this report does not claim

  • Hard syntax failures are a small share of errors (18 of 308). Most errors were

ordinary: test suites exiting 1, greps matching nothing, missing files. The
argument rests on adaptation cost, not error count.

  • Numbers are ±a few calls across parser revisions. An earlier count of mine was

3× too high before correction (a regex matching ?? counted git's untracked-file
markers as PowerShell null-coalescing). Figures above are post-correction and
based on command shape rather than output text.

What Should Happen?

  1. The PowerShell tool should launch pwsh (PowerShell 7) when it is installed,

falling back to powershell.exe only when it is not. Today it always uses
Windows PowerShell 5.1, whose feature set has been frozen since 2016.

  1. Reading and writing files through the PowerShell tool should preserve the

file's encoding. Today the idiomatic 5.1 form (Get-Content -Raw /
Set-Content -Encoding utf8) decodes as the ANSI codepage and writes back
UTF-8 with a BOM, corrupting every non-ASCII character in a tracked source
file, with no error at any stage and no failed syntax check afterwards.

  1. A command written in POSIX form should be routed to the Bash tool where one is

available, rather than executed in PowerShell where it cannot parse. The
failure should be closed (routed or refused), not open (executed and mangled).

  1. A multi-line string — a commit message in particular — should survive being

passed to a command. Today a PowerShell here-string terminates at the first
double quote inside it, and the remainder of the message reaches git as
pathspecs.

  1. Windows setup should surface that PowerShell 7 exists. On a stock Windows 11

machine, 5.1 is presented simply as "PowerShell" and nothing indicates that a
current version is available separately.

  1. Working with a repository on Windows should not require the user to accumulate

per-project instruction files that encode shell workarounds. Six projects on
this machine hold 35 such files; four independently derived "never chain shell
commands" under four different names.

Error Messages/Logs

Samples are verbatim, from session transcripts under
~/.claude/projects/**/*.jsonl (106 sessions, 2026-07-15 to 2026-08-04) and from
a controlled reproduction run today.

1. SILENT ENCODING CORRUPTION (reproduced on demand; see Steps to Reproduce).
   A Get-Content/Set-Content round trip over a clean UTF-8 file:

   before:  alpha — beta § gamma → delta
            bytes 616c706861 20 e28094 20 6265746120 c2a7 ...   BOM: False

   after:   ALPHA â€" beta § gamma â†' delta
            bytes efbbbf 414c504841 20 c3a2e282ace2809d ...     BOM: True

   PowerShell printed "done". No error, no warning.

2. The same defect against a real 11,400-line tracked source file, edited in
   three places. It passed `python -m py_compile`. The only signal was the
   diff size:

   $ git diff --numstat
   1642    1309    make_vobs.py

   Manual recovery required reversing the round trip, which is itself lossy
   for the five cp1252-undefined byte values:

   'charmap' codec can't encode character '\x81' in position 52146

3. HERE-STRING QUOTING. A commit message containing double quotes terminates
   the here-string early; the remaining words reach git as pathspecs:

   error: pathspec 'rows' did not match any file(s) known to git
   error: pathspec 'contain.' did not match any file(s) known to git

4. INLINE PYTHON through the PowerShell tool:

   File "<string>", line 7
       print(f'{" film\:44s}
                           ^
   SyntaxError: unterminated string literal (detected at line 7)

   py.exe : ScriptBlock should only be specified as a value of the Command parameter.

5. NOTABLE ABSENCE: there are no `&&` parser errors in my logs. Sessions had
   already been instructed to prefer the Bash tool, so PowerShell's limits stop
   producing errors and start producing avoidance — see the 76% -> 82% Bash
   shift in the issue body. Issue #83889 has a first-hand `&&` failure.

Steps to Reproduce

Environment: Windows 11 (any build), no PowerShell 7 installed, so the
PowerShell tool uses Windows PowerShell 5.1. Verify with:
$PSVersionTable.PSVersion -> 5.1.26100.2161

  1. Create a UTF-8 text or source file containing any non-ASCII character

(an em-dash is enough):

alpha — beta § gamma → delta

Confirm it is clean UTF-8 with no BOM:
bytes: 616c706861 20 e28094 20 6265746120 c2a7 ...
BOM: False

  1. Ask Claude Code to make a small textual edit to that file. On Windows it

may reach for the PowerShell tool and run the idiomatic form:

$t = Get-Content $p -Raw
$t = $t.Replace('alpha','ALPHA')
Set-Content $p -Value $t -Encoding utf8

  1. The command reports success. No error, no warning, and the file still

parses — python -m py_compile passes on a .py file.

  1. Inspect the bytes. Every non-ASCII character is corrupted and a BOM has

been prepended:

ALPHA â€" beta § gamma â†' delta
bytes: efbbbf 414c504841 20 c3a2e282ace2809d ...
BOM: True

Cause: 5.1's Get-Content decodes as the ANSI codepage rather than UTF-8,
and Set-Content -Encoding utf8 writes UTF-8 with a BOM.

  1. On a real file the only visible signal is diff size. In my case this hit

an 11,400-line tracked source file during three string replacements and
produced a 1,642-line diff, which is the only reason it was caught before
being committed.

Claude Model

Not sure / Multiple models

Is this a regression?

No, this never worked

Last Working Version

_No response_

Claude Code Version

Claude 1.24012.11 (desktop app, MSIX: Claude_1.24012.11.0_x64__pzs8sxrjxfjjc) No standalone CLI installed — Claude Code is running inside the desktop app.

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

PowerShell

Additional Information

_No response_

View original on GitHub ↗

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