Each block below goes into the field of the same name in the Bug Report template. {NEW} is the number of the new issue.
Posted as bun#44693.
Title
Windows: Bun switches the console code page to 65001 at startup, which garbles output and breaks other processes in the same console (root cause of #43660)
What version of Bun is running?
1.4.2+744846f84 (also reproduced on 1.4.3-canary.1+bbdc5a519)
What platform is your computer?
Microsoft Windows NT 10.0.26300.0 x64
What steps can reproduce the bug?
At startup Bun saves the console's input/output code pages and sets both to 65001; at exit it restores the saved values ([`init()`](https://github.com/oven-sh/bun/blob/3f1765a6de030d00a98c33ff0c776c7a7e4b23e9/src/bun_core/output.rs#L561-L567), [`restore()`](https://github.com/oven-sh/bun/blob/3f1765a6de030d00a98c33ff0c776c7a7e4b23e9/src/bun_core/output.rs#L517-L524)). The code page belongs to the console and is shared by every process attached to it, so two Bun processes in one console overwrite each other's saved value. This is the root cause of #43660.
Open a **new `cmd.exe` window** (any code page other than 65001; 437 on English Windows, 932 on Japanese Windows) and run:
```bat
bun -e "console.log('abc \u6771\u4eac\u5927\u962a xyz'); setTimeout(() => {}, 2000)" | (ping -n 2 127.0.0.1 >nul & bun -e "let d = ''; process.stdin.on('data', c => d += c); process.stdin.on('end', () => setTimeout(() => process.stdout.write(d), 800))")
chcp
```
The `ping` and the timers only fix the order: the writer starts first, the reader starts about 1 s later, and the writer exits while the reader is still running. This reproduced every time (3 out of 3 at code page 437, 3 out of 3 at 932, on both builds).
What is the expected behavior?
The text is printed correctly and the console keeps its code page. This is what Node.js prints when both `bun -e` are replaced with `node -e`, and Deno prints the same with `deno eval`:
```
abc 東京大阪 xyz
Active code page: 437
```
What do you see instead?
At code page 437:
```
abc 東京大阪 xyz
Active code page: 65001
```
At code page 932 (Japanese Windows):
```
abc 譚ア莠ャ螟ァ髦ェ xyz
Active code page: 65001
```
What happens:
1. The writer starts, saves 437 and sets 65001
2. The reader starts and saves 65001 as its "original" value
3. The writer exits and restores 437
4. The reader writes raw UTF-8 bytes to a console that is now at 437, so they are garbled
5. The reader exits and restores 65001, which stays on the console after both are gone
Posted as part of bun#44693.
**Request: please don't change the console code page at all.** Write console output with `WriteConsoleW` (UTF-8 → UTF-16), write bytes unchanged to pipes and files, and read console input with `ReadConsoleW`. None of these depend on the code page. Node.js and Deno never change it and print correctly in every case below. GitHub code search finds no use of `WriteConsoleW` in the repository.
Saving and restoring cannot be made safe: several processes in one console save and restore the same shared value, so the exit order decides what remains, and a forced kill skips the restore entirely (the [source comment](https://github.com/oven-sh/bun/blob/3f1765a6de030d00a98c33ff0c776c7a7e4b23e9/src/bun_core/output.rs#L482-L484) already says restoration "may not be applied if the process is killed abruptly"). Source links point to the current `main`.
**Harms measured** on Windows 11 (10.0.26300), conhost, in consoles started at code page 932 and at 437, Bun 1.4.2 and canary, compared with Node.js v26.10.0 and Deno 2.9.7. Each process read `GetConsoleCP` / `GetConsoleOutputCP` itself, and the screen was read back with `ReadConsoleOutputCharacterW`. Node.js and Deno showed none of these, and every fixed-order verdict was the same at 932 and 437.
1. **Garbled output** in a piped peer (the #43660 symptom), also when the two Bun processes are not directly connected (`bun | node | bun`).
2. **Results change from run to run.** The #43660 command, run 10 times in fresh consoles, was garbled 6 times on 1.4.2 and 7 times on canary at 932, and 3 and 9 times at 437; the counts change on every run. Node.js and Deno were correct every time. With the start/exit order fixed, every order gave the same result 3 out of 3 times.
3. **65001 is left on the console** after a normal exit in some orders, and after a forced kill (`taskkill /F`).
4. **Other programs in the same console break** once 65001 is left: `cmd.exe` misreads the rest of a Shift_JIS batch file (Japanese lines become `�������`), and `chcp` and `pause` switch their messages to English.
5. **The data in the pipe is never corrupted.** Every harm comes from changing shared console state.
**Evidence**
- Full results (tables, timelines, screens; every result links to its evidence file and to the test code): https://lightspeedc.com/20261003-bun-codepage-problem/research/codepage-test-results.html
- Test code that reproduces all of the above in fresh consoles: [`tests/codepage.test.ts`](https://github.com/LightSpeedC/20261003-bun-codepage-problem/blob/5b9f56b36bce4914d1bf90eff7e755022ccd1967/tests/codepage.test.ts) (repository: https://github.com/LightSpeedC/20261003-bun-codepage-problem )
- To run everything on any Windows PC (it downloads pinned Bun, Node.js, Deno and pwsh into the repository and does not use what is installed): `tools\10_setup\setup-runtimes.cmd`, then `tools\40_test\run-tests.cmd`
- Related: #43660
Posted as a comment on bun#43660, with {NEW} replaced by 44693.
Follow-up: the garbling reported here is one symptom of a broader problem. Bun saves and changes the console code page at startup and restores it at exit, but the code page is shared by every process in the console, so concurrent Bun processes overwrite each other's saved value.
I filed #{NEW} with a deterministic reproduction (it also reproduces at code page 437 on English Windows) and measurements against Node.js and Deno. If Bun stops changing the code page, this issue goes away too.
Posted as a comment on bun#44693. When this issue was filed, PR #43662 (the open fix for bun#43660) had been missed. Its build was then measured with the same suite.
Update: when I filed this I missed that #43660 already has an open fix, #43662. I ran its Windows x64 CI build (1.4.3-canary.1+576eb251a) through the same test suite, in consoles started at code page 932 and at 437.
**What #43662 fixes** (every case, at both code pages):
- `console.log` / `process.stdout` output is no longer garbled in any start/exit order, including the #43660 command (correct in all 20 runs, 10 at each code page) and `bun | node | bun`.
- With a fixed start/exit order, 65001 is no longer left on the console after a normal exit.
**What remains with #43662:**
1. Bun still switches the console to 65001 at startup (input and output, read from inside the process), so every other process in that console sees 65001 while Bun runs.
2. A forced kill (`taskkill /F`) still leaves 65001 behind, and a batch file run afterwards in the same console is misread.
3. `fs.writeSync(1)`, `fs.write(1)` and `Bun.write(Bun.stdout)` still go through `WriteFile`. With the writer exiting first, their output is garbled (3 of 3 runs for each API, at 932 and at 437), as the PR description and its review say.
4. Restoring "only a code page this process changed" can still lose the race. In 1 of the 20 runs of the #43660 command, the output code page stayed at 65001 after both processes exited, while the input code page was back at 932. A likely order: the reader saves the output code page after the writer has set 65001, the writer exits and restores 932, then the reader sets 65001 and, having saved 65001, never restores it.
So the request here stays the same: don't change the console code page at all, and write all console output with `WriteConsoleW`. Then the save/restore logic is not needed, and a forced kill leaves nothing behind.
Results with a column for #43662, each result linked to its evidence file: https://lightspeedc.com/20261003-bun-codepage-problem/research/codepage-test-results.html#ch02
Posted as a comment on PR #43662.
I ran this PR's Windows x64 build (1.4.3-canary.1+576eb251a) through a reproducible test suite in consoles started at code page 932 and at 437, next to Bun 1.4.2, canary, Node.js and Deno.
It fixes #43660: `console.log` / `process.stdout` output was correct in every start/exit order, including the #43660 command (20 of 20 runs).
Four things remain:
1. Bun still sets the console to 65001 at startup, so other processes in the same console see 65001 while Bun runs.
2. A forced kill (`taskkill /F`) leaves 65001 behind.
3. `fs.writeSync(1)`, `fs.write(1)` and `Bun.write(Bun.stdout)` still use `WriteFile` and are garbled when the writer exits first.
4. The "restore only if this process changed it" check can still lose the race: 1 of the 20 runs left the output code page at 65001.
I filed #44693 asking Bun not to change the console code page at all; the details and the evidence are there.
An answer to robobun's reply, which asked for the start and exit order of every process on the console in the run that left 65001. Posted as a reply on PR #43662.
Thanks for the detailed answers.
**1–3:** Understood. I'll leave the startup 65001 question to the maintainers in #44693, and I understand that `fs.writeSync` / `fs.write` / `Bun.write` will be handled in `src/sys/windows/fs.rs::write_at` after #42819.
**4:** I re-ran the unordered case with the same build (`1.4.3-canary.1+576eb251a`, console starting at 932) 60 times. A Node.js watcher on the same console logged every process attach/detach and every code page change with a UTC timestamp.
The code page was left changed in 3 of 60 runs: in 932 / out 65001 once, and in 65001 / out 932 twice. In all three, the only processes on the console were cmd.exe, the Node.js probes, `chcp` and the two bun processes. No third process started while the console was at 65001. In each of them, the second bun started 8–11 ms after the first had set 65001.
Timeline, evidence and the watcher code:
https://lightspeedc.com/20261003-bun-codepage-problem/research/codepage-test-results.html#asis-timeline
The order behind the three timelines, derived from the PR's source. Posted as a follow-up on PR #43662.
A follow-up on point 4: I matched the three timelines against the PR's source (head `576eb251a`), and each of them has exactly one order that can produce it.
`init()` ([output.rs L581–L585](https://github.com/oven-sh/bun/blob/576eb251af6a8e7deb961d17cf839ad393c5cf73/src/bun_core/output.rs#L581-L585)) makes four separate calls: save output CP, set output to 65001, save input CP, set input to 65001. `restore()` ([L536–L544](https://github.com/oven-sh/bun/blob/576eb251af6a8e7deb961d17cf839ad393c5cf73/src/bun_core/output.rs#L536-L544)) sets each side back to its saved value, skipping a side whose saved value is 65001. W is the writer (started first) and R the reader.
**Input left at 65001 (runs 45, 51):**
1. W saves 932 and sets 65001.
2. R saves the input: 65001.
3. W exits and restores the input to 932.
4. R sets the input to 65001.
5. R exits; its saved input is 65001, so it is skipped.
**Output left at 65001 (run 19):** the same order on the output side. R saves the output (65001), W restores it to 932, then R sets it to 65001. The input came back in that run, because R saved the input only after W had restored it.
So the race does not need a third process. Whenever the first process's restore lands between the second process's save and set, the second one records 65001 as the "original", and the `!= CP_UTF8` check then skips restoring it. I don't see a per-process check that closes this window, because save-then-set is not atomic across processes that share the console. That is the reason #44693 asks Bun not to change the code page at all.
This is a deduction from the source and the timestamps, not a per-call trace. The watcher cannot see which process made a call, and the step between R's save and set is shorter than its polling interval. The timelines and the derivation: https://lightspeedc.com/20261003-bun-codepage-problem/research/codepage-test-results.html#asis-timeline