Skip to content

a batch file with a non-ASCII path dies with 9009, and adding a BOM to fix it fuses onto the first line and kills it differently

encoding · case

bothmisleading-errorscriptciagentfirst-partyrepro: historicaldefault-encoding
Affectscmd, windows, korean codepage
Fails asCOMMAND NOT RECOGNIZED, MOJIBAKE
Mechanismdefault encoding
Safe fixchcp first line

You generate a .cmd wrapper — an autostart shim, a service launcher, a scheduled-task entry — and it works on your machine and fails on someone else’s. The exit code is 9009, which cmd.exe means as “command not recognized”, so you go hunting for a missing binary that is right there on disk.

The actual difference is that their user name, or their install directory, is not ASCII. Every path in the file that contains a Korean, Japanese, or accented character arrives at cmd.exe as mojibake, and the path it tries to run does not exist.

Then you fix the encoding the obvious way, save the file as UTF-8 with a BOM, and it breaks again — differently.

Write a wrapper containing a non-ASCII path and run it from a console whose OEM codepage is not UTF-8 (the default on most non-English Windows installs):

PS> $body = "@ECHO OFF`r`nECHO C:\Users\정준\app`r`n"
PS> [IO.File]::WriteAllText("t.cmd", $body, [Text.UTF8Encoding]::new($false))
PS> cmd /c t.cmd
C:\Users\ъ á\app

Now save it WITH a BOM and watch the failure move:

PS> [IO.File]::WriteAllText("t.cmd", $body, [Text.UTF8Encoding]::new($true))
PS> cmd /c t.cmd
'@ECHO' is not recognized as an internal or external command,
operable program or batch file.

The exact garbage in front of @ECHO is whatever your console codepage makes of the three BOM bytes EF BB BF on a 437 or 850 console, something else on 949 or 932. The shape is the same everywhere: the BOM became characters, they fused onto the first token, and cmd.exe went looking for a command by that name.

cmd.exe reads a BOM-less batch file in the console’s OEM codepage — 437 or 850 on Western installs, 949 on Korean, 932 on Japanese — not in UTF-8. Bytes you wrote as UTF-8 are decoded as something else, and any non-ASCII path becomes a different path. Which mojibake you get depends on the console, which is why the same file “works” for you and not for the next person.

A BOM does not opt you into UTF-8. cmd.exe has no BOM handling for batch files, so the three BOM bytes are simply the first three characters of the first line. They fuse onto whatever follows — @ECHO OFF becomes ∩╗┐@ECHO OFF — and the interpreter reports a command it cannot find. Same 9009, new reason.

This is what makes it expensive to diagnose: both spellings fail with the error that means “your program is missing”, and neither mentions encoding.

Write the file BOM-less and switch the codepage before any line that carries non-ASCII text:

Terminal window
@ECHO OFF
chcp 65001 >nul
REM every line below is read as UTF-8

Put chcp first, above comments as well as commands. Keeping the whole preamble ASCII costs nothing, and it removes the question of exactly when each line is decoded — a detail that is easy to get wrong and hard to verify.

The alternative, when you control the content, is to keep the file pure ASCII — resolve paths at runtime through %LOCALAPPDATA% and friends instead of baking them in as literals.


bom-less-ps1-cp949 is the PowerShell half of this: a .ps1 without a BOM gets read in the ANSI codepage. This is the cmd.exe half, and the fix is inverted — a BOM helps PowerShell 5.1 and actively breaks a batch file.