AIのエラーと対処

Claude Codeが書いた.ps1が文字化けして動かない(PowerShell 5.1)。先頭にBOMを付けたら直った

運営者

2026年10月10日、部下役のAI(Claude Code)に書かせたPowerShellのスクリプトが、何も言わずに失敗していました。
中に書いた日本語のフォルダ名が、読み込みの段階で化けていたのです。
直したのは、ファイルの先頭にBOMという3バイトの印を足すことだけでした。

BOMは、テキストファイルの頭に置いて、中身がUTF-8だと読み手に知らせる印です。
中身は EF BB BF の3バイトで、画面には出ません。
PowerShell 5.1は、この印がない.ps1を、日本語版Windowsの古い文字コード(cp932)で読みます。
cp932は、Shift_JISをもとにしたWindows用の日本語の文字コードです。
UTF-8で書いた日本語をcp932として読むと、別の文字に化けます。

区切りの行が、いつまでもログに出てこなかった

この日は、日本語学習チャンネルの聞き流し動画(約2時間)の声を作っていました。
声はGeminiのTTS(文字を読み上げ音声にする機能)で作ります。
1日に送れる回数に上限があるので、当たったら65分待って続きから再開する作りにしました。

部下役のAIがコマンドを動かす道具は、1回に10分までしか待てません。
そこで、PowerShellのスクリプトを Start-Process で切り離して走らせました。
スクリプトは、試すたびにログへ区切りの行(マーカー)を書き足す作りです。
書き足しには、.NETの [IO.File]::AppendAllText を使っていました。

ところが、その区切りの行が残りません。
エラーの表示も見当たりませんでした。
切り離した処理なので、何か出ていても誰の目にも触れないのです。

ヒアドキュメントで書いた.ps1は、BOMなしのUTF-8になる

原因は、スクリプトの書き方にありました。
部下役のAIは、Bash(文字で命令を打つ画面の一種)のヒアドキュメントで.ps1を作っていました。
ヒアドキュメントは、cat > script.ps1 <<'EOF' のように複数行の文章をそのままファイルへ流し込む書き方です。
こうして作ったファイルは、BOMの付かないUTF-8になります。

PowerShell 5.1は、BOMのない.ps1をUTF-8としては読みません。
公式の説明では、BOMがないとWindows PowerShellは、スクリプトを古いANSIコードページのものと「誤って解釈」します。
同じページには「ANSI は、PowerShell エンジンがファイルからソース コードを読み取るときにも使用します」とあります。
ANSIコードページは、Windowsが言語の設定ごとに使い分けてきた、古い文字コードの番号です。
僕のPCのPowerShellで調べると、この番号は932(cp932)でした。

スクリプトの中に書いた日本語のパスは、ここで化けます。
10月10日の記録にも、日本語のパスが壊れ、AppendAllTextが黙って失敗したと残っています。

11月10日に、同じ形で再現した

記事にするにあたり、部下役のAIに同じ形のスクリプトを作らせました。
日本語のフォルダ名を含むパスへ、区切りの行を1行書き足すだけのものです。
BOMなしで保存し、PowerShell 5.1で実行しました。

パスの「デスクトップ\作業用」は、PowerShellの中でこう化けています。

繝・せ繧ッ繝医ャ繝予菴懈・ュ逕ィ

フォルダの区切りの \ まで消えました。
「プ」の最後の1バイトと \ が組になり、cp932の「予」の1文字として読まれたためです。
出たエラーはこれです(パスの前半は省きました)。

"3" 個の引数を指定して "AppendAllText" を呼び出し中に例外が発生しました: "パス '…\繝・せ繧ッ繝医ャ繝予菴懈・ュ逕ィ\run.log' の一部が見つかりませんでした。"

化けたパスのフォルダが見つからない、というエラーです。
それでもスクリプトは止まらず、最後の行まで進みました。
終了コード(処理の成否を表す番号)は0で、成功と同じ扱いになります。
切り離して走らせていると、ここで気づく手がかりがありません。
10月10日に区切りの行が黙って消えたのも、この形だったと見ています。

日本語の直後に閉じる引用符が来ると、文字によっては壊れ方が変わります。
Write-Output "完了" の1行だけのファイルでも、PowerShell 5.1はこう返しました。

+ Write-Output "螳御コ・
+              ~~~~~
文字列に終端記号 " がありません。

終端記号は、文字列を閉じる引用符のことです。
「了」の最後の1バイトが閉じる引用符を巻き込み、最後の「・」の1文字に読まれていました。
構文エラーのときは、スクリプトは1行も動かずに終わります。
部下役のAIの道具から動かしたときは、同じエラーが英語の「The string is missing the terminator」で出ました。
9月29日の記録にも、.ps1はBOM付きで保存しないと「日本語が化けて構文エラー」になる、とあります。

「開く」が「髢九¥」になり、ダイアログが見つからなかった

11月4日には、別の形で出ました。
動画のアップロードを手伝わせる補助スクリプトが、Windowsの「開く」ダイアログを見つけられなかったのです。
このスクリプトは、題名が「開く」の窓を探す作りでした。
BOMなしで保存されていたので、探す文字の「開く」そのものが化けていました。

再現でも、Write-Output "開く" は 髢九¥ と表示されています。
これでは、本物の「開く」の窓と一致しません。
このときもBOMを付けて直しました。

先頭にBOMを足す

10月10日は、Pythonで先頭に3バイトを足してから起動しました。
Git Bash(Windowsで使えるBashの一つ)なら、次の1行で足せます。
すでにBOMがあるファイルには、何もしません。

python -c "import sys; p=sys.argv[1]; b=open(p,'rb').read(); open(p,'wb').write(b if b.startswith(b'\xef\xbb\xbf') else b'\xef\xbb\xbf'+b)" script.ps1

PowerShellの中で直すなら、こう書けます。

$p = (Resolve-Path .\script.ps1).Path
$t = [IO.File]::ReadAllText($p, [Text.Encoding]::UTF8)
[IO.File]::WriteAllText($p, $t, (New-Object Text.UTF8Encoding $true))

New-Object Text.UTF8Encoding $true の $true が、BOMを付ける指定です。
どちらの方法も、11月10日に2回続けて実行し、BOMが二重にならないことを確かめました。
読み込みに Get-Content を使うなら、-Encoding UTF8 を付けてください。
公式の説明では、BOMのないファイルを Get-Content はANSIで読むとされています。

付いたかどうかは、先頭の3バイトで確かめられます。

Get-Content -Encoding Byte -TotalCount 3 .\script.ps1

239 187 191 と出れば、BOMが付いています。
Git Bashなら head -c 3 script.ps1 | od -An -tx1 で、ef bb bf と出ます。

再現のファイルにBOMを足すと、日本語のパスはそのまま通りました。
区切りの行も、狙ったフォルダのログに書き込まれています。
10月10日の本番も、BOMを足した版ではログに区切りの行が残りました。
最初に残った区切りの行は「===== try 1 2026-10-10 06:50 =====」です。
声の生成と仮の組み立てはその日のうちに終わり、完了の印のファイルは20時35分にできています。
BOMを足す直前に、パスを Join-Path で組む形へ直した版では、区切りの行はまだ残っていませんでした。
パスの書き方を疑った話は、ヒアドキュメントの記事に書いています。

ログの日本語で、成否を判定しない

10月10日の記録には、もう一つの約束ごとが残っています。
完了したかどうかは、ログの日本語ではなく、動画ファイル(mp4)ができたかで見ることです。
上限に当たったかどうかも、英数字だけの Rate limit か 429 で探しています。

PowerShell 5.1の >>(ファイルへの書き足し)は、UTF-16LEという別の文字コードで書きます。
公式の説明にも、Out-Fileと > >> はUTF-16LEを作る、とあります。
UTF-8で書くPythonの出力と同じログに混ぜると、1つのファイルの中で文字コードがばらばらになるわけです。
そこで、Pythonの出力は cmd /c を通して、PowerShellを経ずにログへ書かせました。

書かせる前に決めておくこと

部下役のAIの作業メモには、.ps1はBOM付きで保存する、という決まりを残しています。
11月10日に作り直したアップロード用の補助スクリプトも、メモに「BOM 付きで保存すること」と書き添えています。
動画まわりの補助スクリプト9本を同じ日に確かめると、すべて先頭がBOMでした。

公式の説明では、BOMなしのUTF-8のファイルは「PowerShell では正常に動作」するとされています。
壊れる可能性があるのは、日本語のような文字を含むときの「Windows PowerShell」のほうです。
前者は新しい版(7など)、後者はWindowsに最初から入っている5.1までを指す呼び名です。
僕のPCには新しい版が入っていないので、そちらでは試していません。

参考

この日に待っていたGeminiの1日の上限は、9月26日に当たったときの記事に書きました。
Gemini TTSが429エラーで止まった。1日100回の上限に、朝のテストも入っていた

切り離したPowerShellでは、ほかに2つの止まり方もしました。
PowerShellのStop-Processで自分自身が止まる。exit 255で後続が消えた原因と直し方
OneDrive配下でPowerShellの削除・移動が「アクセスが拒否されました」で止まる。Pythonで移して直した

記事URLをコピーしました