Windows: harness sets $OutputEncoding but not [Console]::OutputEncoding, leaving PowerShell tool output mojibake after any chcp
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:
$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().[Console]::OutputEncoding(.NET console-layer property) — controls how the process writes text bytes to the actual console output handle. This is what determines whetherWrite-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 fromGetConsoleOutputCP()— 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
- On a Windows system with any non-English system locale (repro system: ACP=950 / Traditional Chinese Big5), start a Claude Code session.
- 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). - 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.
- Run any subsequent PowerShell tool call that writes non-ASCII (e.g. CJK) text.
- Actual: Output renders as
�replacement characters — mojibake. This persists for every PowerShell tool call for the rest of the session, because each newpwsh.exeprocess inherits the now-poisoned console code page viaGetConsoleOutputCP(), and[Console]::OutputEncodingis never explicitly reset to UTF-8 by the harness. - Expected: PowerShell tool output should render correctly regardless of any earlier
chcpcall 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 invokespwsh.exewith-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]::OutputEncodingis a runtime API call, not something settable via env var, andGetConsoleOutputCP()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
chcpinterference); proposedCLAUDE_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 anychcpcall, not just a systemic default-locale issue. - #46486 (closed as duplicate of #43024) — reported plain
powershell.exe -NoProfile -Commandgarbling 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.