BOM は JSON のパースを壊すか
要点:UTF-8 BOM は見えない 3 バイトなので、`JSON.parse` が最初の文字で失敗し、1 行 1 列目を報告します。そのためファイルが空や途中切れに見えます。

要点:先頭 3 バイトが EF BB BF かを確認し、BOM なしの UTF-8 で再保存しましょう。サーバーでは、アップロードやサードパーティのファイルをパースする前に先頭の U+FEFF を除去し、後で失敗をデバッグしないようにします。
壊します。しかもファイルが完全に正常に見えるため、すべてのパースエラーの中で最も混乱を招くものを生みます。
見えるもの
SyntaxError: Unexpected token in JSON at position 0
Unexpected character '\uFEFF'
メッセージ内のその文字は見えません。position 0 は最初のバイトを意味します。
cat は何も異常を示しません。hexdump -C file.json | head -1 は示します。
00000000 ef bb bf 7b 0a 20 20 22 61 22 3a 20 31 0a 7d 0a |...{ "a": 1.}.|
7b は { です。その前の 3 バイトが BOM です。
BOM はどこから来るか
バイトオーダーマークは UTF-16 のために存在し、そこではバイト順が実際に変わりました。UTF-8 は単一のバイト順なので、BOM は純粋なオーバーヘッドです。それでも Windows のメモ帳、一部の Excel エクスポート、いくつかの古い IDE は今も既定で書き込みます。
RFC 8259 は曖昧ではありません。システム間で交換する JSON は UTF-8 でなければならず、実装は BOM を付けてはなりません。パーサーは先頭 BOM を無視してもよいが、必須ではありません。ブラウザと Node は無視しないことを選んでいます。
除去する三つの方法
BOM なしで保存する。 VS Code ではステータスバーのエンコーディングをクリックし、「エンコーディング付きで保存 → UTF-8」を選びます。「UTF-8 with BOM」ではありません。
シェルで除去する。
sed -i '1s/^\xEF\xBB\xBF//' data.json
読み取り時に除去する。 アップロードやサードパーティのファイルを受け入れるものには、これが持続的な修正です。
const text = require('node:fs').readFileSync('data.json', 'utf8').replace(/^\uFEFF/, '');
JSON.parse(text);
Python では open(path, encoding='utf-8-sig') が自動で扱います。
似た二つの問題
- ゼロ幅スペース(U+200B):Web ページから JSON をコピーするときに混入します。症状は同じでコードポイントが違います。
- 全角引用符:日本語入力が
"key"を“key”に変え、ほとんど同じに見えるのにUnexpected token “で失敗します。
三つとも不可視文字のバグです。診断は常に同じで、テキストを見つめるのではなく先頭バイトを 16 進で出力します。
FAQ
ファイルが BOM で始まるかを見分けるには?
cat は何も示さないので、hexdump -C file.json | head -1 を実行します。出力が ef bb bf で始まれば BOM があり、きれいな UTF-8 ファイルでは 7b({)が先頭に来るはずです。
エラーが常に 1 行 1 列目を指すのはなぜですか?
BOM が他のすべての前に座るので、パーサーが最初に読む文字が U+FEFF です。失敗はファイル先頭に着地し、ファイルが空に見えます。
パース前に BOM を除去すべきですか?
はい。サーバーが設定を読むときやアップロードファイルをパースするときは、先頭の U+FEFF を除去します。Node なら replace(/^\uFEFF/, '')、Python なら encoding='utf-8-sig'。後で診断するよりずっと安上がりです。