Windows: harness sets $OutputEncoding but not [Console]::OutputEncoding, leaving PowerShell tool output mojibake after any chcp

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 25, 2026

Summary

On Windows, the PowerShell tool's launch command sets $OutputEncoding (the pipeline/formatting-layer encoding) to UTF-8 on every invocation, but never sets [Console]::OutputEncoding (the console-layer encoding, which .NET initializes from the process's inherited console output code page). These are two distinct encoding properties in .NET/PowerShell and fixing one does not fix the other.

Because the console output code page is a shared, persistent OS console property (not per-process state), if it is ever changed mid-session by any means — most simply, a user or the agent running chcp <code page> inside a PowerShell/cmd tool call for a legitimate reason (e.g. diagnosing a non-UTF-8 environment) — every subsequent pwsh.exe process spawned by the harness inherits the changed code page. Since [Console]::OutputEncoding is never explicitly reset by the harness, all PowerShell tool output becomes mojibake ( replacement characters) for any non-ASCII text (in our case, CJK/Chinese) from that point forward, for the rest of the session.

This is a related-but-distinct root cause from prior encoding reports (see "Related issues" below): it's not that the harness fails to set UTF-8 at all — it correctly sets $OutputEncoding on every call — it's that it sets only one of the two relevant encoding properties, so the fix is incomplete and the console layer can be silently poisoned by an out-of-band chcp call.

Root cause detail: pipeline layer vs. console layer

PowerShell/.NET has two separate encoding settings that both affect what actually reaches the terminal:

  1. $OutputEncoding (PowerShell pipeline-layer variable) — controls how PowerShell encodes text when piping to external/native programs. The harness sets this every time: $OutputEncoding = [System.Text.UTF8Encoding]::new().
  2. [Console]::OutputEncoding (.NET console-layer property) — controls how the process writes text bytes to the actual console output handle. This is what determines whether Write-Host, Write-Output, etc. render correctly on screen or when captured by the harness. This is never set by the harness. Its default value comes from GetConsoleOutputCP() — the OS-level console output code page inherited from the parent console session.

Setting only $OutputEncoding fixes encoding for data flowing through the PowerShell pipeline to external processes, but does not fix the console-layer rendering of the command's own direct output — which is what actually reaches the harness/terminal.

Reproduction

  1. On a Windows system with any non-English system locale (repro system: ACP=950 / Traditional Chinese Big5), start a Claude Code session.
  2. At any point during the session, run a PowerShell (or cmd) tool call that executes chcp <some code page> for any reason (this can happen incidentally, e.g. while debugging environment/locale issues).
  3. Observe: output from that command may look fine (the command itself may set encoding internally), but the console output code page for the shared console session is now changed.
  4. Run any subsequent PowerShell tool call that writes non-ASCII (e.g. CJK) text.
  5. Actual: Output renders as replacement characters — mojibake. This persists for every PowerShell tool call for the rest of the session, because each new pwsh.exe process inherits the now-poisoned console code page via GetConsoleOutputCP(), and [Console]::OutputEncoding is never explicitly reset to UTF-8 by the harness.
  6. Expected: PowerShell tool output should render correctly regardless of any earlier chcp call within the session, since the harness already goes to the trouble of forcing UTF-8 via $OutputEncoding.

Minimal illustration of the two-property gap (run in any pwsh -NoProfile session with a non-UTF-8 system code page):

$OutputEncoding = [System.Text.UTF8Encoding]::new()  # what the harness currently does
Write-Host "中文測試"                                  # still garbled — [Console]::OutputEncoding untouched
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()  # the missing piece
Write-Host "中文測試"                                  # now renders correctly

Suggested fix

In the PowerShell tool's injected startup snippet, alongside the existing:

try { $PSDefaultParameterValues['Out-File:Encoding'] = 'utf8' } catch {}
$OutputEncoding = [System.Text.UTF8Encoding]::new()

also set:

try { [Console]::OutputEncoding = [System.Text.UTF8Encoding]::new() } catch {}

Alternatively/additionally, normalize the console output code page (chcp 65001 via kernel32!SetConsoleOutputCP) once before spawning pwsh.exe, so a poisoned inherited code page can't propagate into new tool-call processes at all. Setting [Console]::OutputEncoding inside each invocation is the more surgical fix since it doesn't require mutating shared OS console state.

What we tried that does NOT work (to save maintainers time)

  • PowerShell profile script ($PROFILE) — ineffective, because the harness invokes pwsh.exe with -NoProfile (confirmed via the documented launch command; also verified locally that no profile is loaded during tool calls).
  • Environment variables — there is no environment variable that controls the Win32 console output code page. [Console]::OutputEncoding is a runtime API call, not something settable via env var, and GetConsoleOutputCP() reflects live OS console state.
  • System-wide "Use Unicode UTF-8 for worldwide language support" (Beta ACP=65001) — not viable for all users, including us: some local scheduled tasks/scripts rely on the system default Traditional Chinese code page (950) for correctly handling non-ASCII file paths, and forcing system ACP to UTF-8 would break them. A fix should not require the user to change their system locale.

Related issues

This appears to be a more precise/narrower root cause than some prior encoding reports, worth distinguishing:

  • #62862 (closed, stale) — reported PowerShell mojibake during parallel agent execution on a non-UTF-8 baseline system locale (no prior chcp interference); proposed CLAUDE_CODE_POWERSHELL_PATH/shell-init config options. Root cause there (Option C in that issue) is adjacent but not identical: that report is about the default baseline never being UTF-8, whereas this report is about the harness's existing UTF-8 fix being asymmetric (pipeline-only) and therefore reversible mid-session by any chcp call, not just a systemic default-locale issue.
  • #46486 (closed as duplicate of #43024) — reported plain powershell.exe -NoProfile -Command garbling on Big5 (950) systems without going through the actual Claude Code harness invocation; #43024 itself is about a Stop-hook PowerShell parser error with Korean text, a different symptom/mechanism entirely.

Neither prior report specifically identifies the $OutputEncoding-vs-[Console]::OutputEncoding asymmetry, or the fact that the harness's current partial fix can be silently undone by an in-session chcp call, causing output to work correctly at session start and then degrade partway through — which makes this bug easy to misdiagnose as intermittent/environment-specific rather than a straightforward code gap.

Environment

  • OS: Windows 11 Home, 10.0.26200
  • PowerShell: 7.6.4 (pwsh.exe)
  • System ACP: 950 (Traditional Chinese, Big5)
  • Claude Code: current version as of 2026-07

Impact

Any Windows user on a non-UTF-8 system locale whose session, for any reason, ends up running chcp (directly or via a diagnostic/legacy script invoked through the Bash or PowerShell tool) loses readable PowerShell tool output for the remainder of the session — with no in-session recovery available to the user, since there is no supported way to reset [Console]::OutputEncoding for future harness-spawned processes from inside a session.

View original on GitHub ↗