JSONを扱っていると、一度は遭遇するのが SyntaxError: Unexpected token というエラーメッセージです。API のレスポンスを処理しようとしたとき、設定ファイルを読み込もうとしたとき、突然このエラーが出て作業が止まってしまいます。
手っ取り早くエラー箇所を特定したい場合は、JSON Formatter にペーストすれば、エラーの行番号とともに原因が表示されます。
原因さえわかれば修正は簡単ですが、エラーメッセージだけでは何が悪いのか判断しづらいことも多いです。この記事では、JSON Parse Error の読み方と、よくある原因5つを具体例つきで解説します。
SyntaxError: Unexpected token の読み方
ブラウザやNode.jsでJSONをパースする際、構文エラーがあればエラーが投げられます。次の壊れたJSON(オブジェクトの値がシングルクォートになっている)を例に見てみましょう。
{
"name": "Alice",
"age": 30,
"city": 'Tokyo'
}
現行のV8(ChromeとNode.jsが使うエンジン)は次のメッセージを返します。
Unexpected token ''', ..." "city": 'Tokyo'
}" is not valid JSON
このメッセージは2つの情報を含んでいます。
Unexpected token '''— パーサーが想定していない文字'(シングルクォート)に遭遇した..." "city": 'Tokyo'以降の部分 — エラー箇所の前後を切り取った文字列そのもの
古いバージョンのエンジンは Unexpected token ' in JSON at position 14 のように position の数値(先頭から何文字目か)を報告していましたが、現行のメッセージは数値の代わりにエラー周辺の文字列を引用します。両方の表記は本記事後半の対応表にまとめました。position 付きの表記でも、改行コードや空白もカウントされるため手作業で数えるのは現実的ではありません。JSON Formatter のようなツールを使えば、該当箇所をすぐに特定できます。
よくある原因5つ
1. 末尾カンマ(trailing comma)
JavaScriptでは配列やオブジェクトの最後にカンマを付けても動作します。しかしJSONの仕様では、末尾カンマは許可されていません。
{
"name": "Alice",
"age": 30,
"city": "Tokyo",
}
最後の "Tokyo", のカンマを削除する必要があります。
{
"name": "Alice",
"age": 30,
"city": "Tokyo"
}
"Tokyo" の後ろのカンマが原因です。現行のV8は次のメッセージを返します。
Expected double-quoted property name in JSON at position 53 (line 5 column 1)
パーサーはカンマを読んだあと次のキーと値のペアを期待しますが、実際に来るのは閉じ波括弧です。そのため「クォート付きのプロパティ名を期待していた」という報告になり、閉じ波括弧自体は unexpected token として扱われません。これは配列の末尾カンマとは異なる挙動です。
配列でも同じ間違いが起きますが、こちらは挙動が異なります。
{
"colors": ["red", "green", "blue",]
}
現行のV8はこちらでは unexpected token 形式のメッセージを返します。
Unexpected token ']', ..."", "blue",]
}" is not valid JSON
配列の末尾カンマの後ろに来る実際の文字は ] なので、V8 はそれを unexpected token として報告します。"blue" の後ろのカンマを削除すればパースできます。エディタのフォーマッタが自動で末尾カンマを付ける設定になっている場合、JSON ファイルでは無効にしておくとよいでしょう。
2. シングルクォート
Pythonの辞書やJavaScriptのオブジェクトでは ' を使えますが、JSONではダブルクォート " のみが有効です。
{'name': 'Alice'}
これは正しいJSONではありません。すべてダブルクォートに置き換えます。
{"name": "Alice"}
Pythonで辞書をJSON文字列にする場合は、str() ではなく json.dumps() を使ってください。str() はシングルクォートで出力してしまいます。
3. クォートなしのキー
JavaScriptではオブジェクトのキーにクォートが不要な場合があります。JSONでは、キーは必ずダブルクォートで囲む必要があります。
{name: "Alice"}
正しくはこう書きます。
{"name": "Alice"}
4. コメント
設定ファイルでよくある間違いです。JSONの仕様にはコメント構文が存在しません。
{
// ユーザー名
"name": "Alice"
}
// や /* */ はすべて取り除く必要があります。なお、tsconfig.json など一部のファイルはJSONC(JSON with Comments)という拡張仕様で書かれており、コメントが許可されています。ただし標準のJSONパーサーでは読めません。
JSONC・JSON5・_comment フィールド・除去スクリプトの使い分けは JSON にコメントは書ける? で詳しく比較しています。設定ファイルで「コメントを残したい」要件がある場合はそちらを参照してください。あるいは、JSONではなくYAMLで管理するという選択肢もあります。
5. BOM(Byte Order Mark)
UTF-8で保存したファイルの先頭にBOM(\uFEFF)がある場合、パーサーはそれを不正な文字として検出します。現行のV8は次のメッセージを返します。
Unexpected token '', "{
"name"... is not valid JSON
最初の ' の直後にある、見た目には何も無いように見える文字がBOMです。古いバージョンのエンジンは Unexpected token in JSON at position 0 のように position 0(ファイルの先頭)として報告していました。ファイルから取り除く方法は 3 つあります。
- VS Code: ステータスバーのエンコーディング表示をクリックし、「エンコーディング付きで保存」から BOM なしの「UTF-8」を選ぶ
- コマンドライン:
sed -i '1s/^\xEF\xBB\xBF//' file.json - hex エディタ: ファイル先頭の 3 バイト(UTF-8 BOM の
EF BB BF)を削除する
ファイルの保存方法を制御できない場合(ユーザーのアップロードを読み込むときなど)は、プログラム側で先頭のBOMを除去する処理を入れてください。
const cleaned = text.replace(/^\uFEFF/, '');
const data = JSON.parse(cleaned);
その他の原因
代表的な5つ以外にも、次のような原因でパースエラーが起きます。
括弧の対応ミス
閉じ括弧 } や ] が足りないと、多くの場合ファイル末尾でエラーが報告されます。ネストが深い JSON では、対応の取れていない括弧を目視で探すのは困難です。括弧の対応を表示できるフォーマッタを使うと時間を節約できます。
文字列内の制御文字
文字列の中に生の改行やタブが入っているとエラーになります。改行は \n のようにエスケープする必要があります。
{"message": "Hello
World"}
正しくは次のように書きます。
{"message": "Hello\nWorld"}
先頭ゼロのある数値
JSON の数値は先頭にゼロを付けられません。007 は不正な値で、7 などの標準的な数値表記にする必要があります。
undefined や NaN
JavaScript の undefined と NaN は JSON の値として使えません。undefined を含むオブジェクトをシリアライズすると、多くのシリアライザはそのキーを省略するかエラーを投げます。NaN や Infinity も同様に拒否されます。
旧メッセージと現行メッセージの対応表
古いガイドやキャッシュされたStack Overflowの回答、この記事の以前のバージョンと照らし合わせて手元のコンソールの文言が一致しない場合はこれが理由です。JSON.parse() のエラーメッセージの文言は、JavaScriptエンジンの変化にともなって変わりました。本記事の他の箇所は現行の文言を主に使っていますが、旧文言も検索の手がかりとして残しています。
大きな変化は2つあります。1つ目は、引用符付きトークン形式(Unexpected token 'X', "..." is not valid JSON)が position の数値を一切報告しなくなったことです。代わりにエラー周辺の文字列そのものを引用します。2つ目は、Expected ... in JSON at position N 形式のメッセージは position を報告し続けているものの、現行では (line L column C) が追加で付くようになったことです。これは旧メッセージには無かった情報です。
| この記事内の該当箇所 | 旧メッセージ | 現行V8の出力(実測) |
|---|---|---|
| 冒頭の例(シングルクォート) | Unexpected token ' in JSON at position 14 | Unexpected token ''', ..." "city": 'Tokyo'}" is not valid JSON |
| 原因1: オブジェクトの末尾カンマ | 本文中に引用なし(「閉じ波括弧が unexpected token になる」とだけ説明) | Expected double-quoted property name in JSON at position 53 (line 5 column 1) |
| 原因1: 配列の末尾カンマ | 本文中に引用なし(オブジェクトの場合と同じ説明) | Unexpected token ']', ..."", "blue",]}" is not valid JSON |
| 原因3: クォートなしキー | 本文中に引用なし | Expected property name or '}' in JSON at position 4 (line 2 column 3) |
| 原因4: コメント | 本文中に引用なし | Expected property name or '}' in JSON at position 4 (line 2 column 3) |
| 原因5: BOM | Unexpected token in JSON at position 0 | Unexpected token '', "{ "name"... is not valid JSON |
JSON.parse(undefined) | Unexpected token u in JSON at position 0 | "undefined" is not valid JSON |
JSON.parse({ name: "Alice" }) | Unexpected token o in JSON at position 1 | "[object Object]" is not valid JSON |
| HTMLレスポンス(後述の環境別表) | Unexpected token < in JSON at position 0 | Unexpected token '<', "<!DOCTYPE html>" is not valid JSON |
「現行V8の出力」列の文字列は、この記事内で使っているJSONの例をそのまま JSON.parse() に通して実測したものです(Node v26.3.1 / V8 14.6)。実行スクリプトと出力結果は scripts/benchmarks/json-parse-error-messages/ にコミットしてあるので、どの行も再現できます。どのバージョンのV8からこの文言に変わったかまでは未検証で、この表が確定しているのは「現行V8が今このメッセージを返す」という事実だけです。
症状別の直し方: token u / token o / end of input
エラーメッセージに含まれる文字そのものが、原因を大きく絞り込む手がかりになります。次の 3 つの変種はいずれも JSON ファイルの中身ではなく、「パーサーに渡す前」の段階に問題があるパターンです。なお Unexpected token < が出た場合は JSON ではなく HTML が返ってきたサインです(後述の環境別エラーメッセージ表を参照)。
Unexpected token u in JSON at position 0
JSON.parse に文字列 "undefined" が渡っています。u はその 1 文字目で、古いバージョンのエンジンがエラーメッセージにこの文字を使っていた理由でもあります。パースしようとした値が、その時点で undefined だったことをほぼ確実に意味します。
const raw = localStorage.getItem("settings"); // キーが存在しない場合は null
JSON.parse(undefined); // 投げられるエラー: "undefined" is not valid JSON
現行のV8は1文字だけを名指しするのではなく、変換後の文字列全体 ("undefined" is not valid JSON) を引用します。パースの前に値が本当に存在するか確認してください。本文が空の API レスポンス、存在しないストレージキー、変数名のタイプミスが典型的な原因です。
Unexpected token o in JSON at position 1
"[object Object]" という文字列をパースしようとしたときのエラーです。position 0 の [ は配列の開始として妥当なので、古いバージョンのエンジンはパーサーが position 1 の o で失敗したと報告していました。JSON 文字列ではなく JavaScript オブジェクトそのものを JSON.parse に渡すと、暗黙の文字列変換によってこの形になります。
const data = { name: "Alice" };
JSON.parse(data); // data は "[object Object]" に変換される。投げられるエラー: "[object Object]" is not valid JSON
現行のV8は o という文字を名指しするのではなく、変換後の文字列をそのまま引用します。その値はすでにパース済みです。そのまま使うか、ディープコピーが目的なら stringify / parse の往復ではなく structuredClone(data) を使ってください。
Unexpected end of JSON input
JSON が完結する前に文字列が終わっています。よくあるのは空文字列(JSON.parse(""))と、途中で切れたレスポンス(中断されたネットワークリクエストや書き込み途中のファイル)の 2 つです。まず生の文字列の長さをログに出してください。長さが 0 なら、問題は JSON の構文ではなくパーサーに渡す前の段階にあります。
RapidJSON は同じ状況を The document is empty. と報告します(エラー文言の定義別タブで開きます)。.NET のホストも、runtimeconfig.json が空のときに同じ文言を出します(dotnet/runtime#111231別タブで開きます)。
環境ごとのエラーメッセージの違い
同じ構文エラーでも、エラーメッセージは実行環境によって異なります。検索するときの手がかりになるよう、代表的な環境のメッセージを整理します。
| 環境 / ランタイム | 典型的なエラーメッセージ | 読み取れること |
|---|---|---|
| Chrome / Node.js (V8) | Unexpected token '<', "<!DOCTYPE html>" is not valid JSON | 先頭の < は、JSON ではなく HTML エラーページが返ってきたサイン。ネットワークタブで実際のレスポンスを確認する |
| Firefox (SpiderMonkey) | SyntaxError: JSON.parse: unexpected character | MDN が JSON.parse() のエラーを JSON.parse: <理由> 形式で文書化している (JSON_bad_parse別タブで開きます)。どのメッセージがどのエンジン由来かはMDN側も明示していないため、ここでは「MDNが文書化している形式」として扱い、SpiderMonkeyで実際に確認した文字列とまでは断定しない |
| Python | json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 3 column 5 | 行番号と列番号の両方を表示する |
| Java (Jackson) | JsonParseException: Unexpected character ('}' (code 125)): was expecting double-quote to start field name | 期待した文字と実際の文字の両方を明示する |
| RapidJSON (C++) / .NET のホスト | The document is empty. | 入力が 0 バイト。上の Unexpected end of JSON input と同じ原因なので、パースする前にファイルやレスポンス本文が空でないかを確かめる |
ブラウザで Unexpected token '<', "<!DOCTYPE html>" is not valid JSON(あるいは旧来の Unexpected token < in JSON at position 0、Firefoxの JSON.parse: unexpected character)が出た場合は、API が JSON ではなく HTML のエラーページ(404 や 500 のページ)を返している可能性がほぼ確実です。JSON の構文ではなく、リクエスト先やステータスコードを疑ってください。
デバッグの切り分け手順
エラーが数行のファイルなら目視で直せますが、API レスポンスや大きなファイルでは、切り分けの順序を決めておくと速く解決できます。
- JSON Formatter にペーストして、エラーの行番号と内容を確認する
- 報告された position の少し手前まで確認する。実際のミスはエラー位置より前の行にあることが多い(例: 10 行目のカンマ漏れが 11 行目でエラーになる)
- 上で挙げた原因 5 つ(末尾カンマ・シングルクォート・クォートなしキー・コメント・BOM)に該当しないか確認する
- コードが生成した JSON なら、シリアライズ処理を確認する。文字列連結で JSON を組み立てている箇所は典型的なバグの温床
- API から受け取った JSON なら、パースの前に生のレスポンスとエンコーディングを確認する
fetch でレスポンスを受け取る場合は、response.json() を使う前に生テキストとステータスを確認すると、HTML エラーページ・空レスポンス・途切れたレスポンスを 1 箇所で見分けられます。
const response = await fetch("/api/data");
const raw = await response.text(); // response.json() ではなく、まず生テキストで受ける
if (!response.ok) {
console.error(`HTTP ${response.status}:`, raw.slice(0, 200));
} else if (!raw) {
console.error("レスポンス本文が空"); // Unexpected end of JSON input の典型原因
} else {
try {
const data = JSON.parse(raw);
} catch (e) {
console.error("JSON parse 失敗:", e.message, raw.slice(0, 200));
}
}
FormatArc でエラー箇所を確認する
5つの原因を把握していても、数百行あるJSONファイルから問題箇所を目視で探すのは大変です。JSON Formatter を使えば、ペーストするだけでエラーの行番号と内容が日本語で表示されます。
使い方は3ステップです。
- JSON Formatter を開く
- 左側のエディタにJSONを貼り付ける
- 「変換」ボタンを押す
正しいJSONであれば整形された結果が右側に表示されます。構文エラーがあれば、エラーメッセージに行番号が含まれるので、該当行を修正して再度変換すればよいでしょう。
ブラウザ内で処理が完結するため、APIキーや個人情報を含むJSONでも安心して使えます。データがサーバーに送信されることはありません。
パースエラーを未然に防ぐ
エラーが出てから直すよりも、いくつかの習慣でパースエラーの大半を防げます。
- JSON を生成するときは文字列連結ではなく
JSON.stringify()(各言語の標準シリアライザ)を使う - エディタで保存時に JSON を検証する設定を有効にする(VS Code・IntelliJ・Sublime Text はいずれも対応)
- CI に検証ステップを追加する。
python -m json.tool < config.jsonのような簡単なチェックで、本番に届く前に壊れたファイルを検出できる - 手書きで編集するときは、リアルタイムに検証できるツールを使う
なお、信頼できないオンラインツールに業務データを貼り付けるのは避けてください。どのツールが安全かの見分け方は オンライン変換ツールは安全か で解説しています。FormatArc はすべての処理をブラウザ内で完結し、データを外部に送信しません。
JSON が適さない場合
コメントを書きたい、複数行の文字列を扱いたい、複雑なネスト設定を管理したいといった要件で JSON の制約と頻繁に戦っているなら、YAML を検討する価値があります。YAML はコメントに対応し、複数行テキストも自然に扱え、設定ファイルとしては読みやすいことが多いです。
ただし YAML はインデントに敏感で、暗黙の型変換など独自の落とし穴もあります。両者は必要に応じて相互変換できるので、ケースに応じて使い分ければ十分です。詳しくは YAML と JSON の違い を参照してください。
まとめ
JSON Parse Error の大半は、この記事で紹介した5つのパターンのいずれかに該当します。エラーメッセージの Unexpected token と position を手がかりに原因を絞り込めますが、ファイルが大きい場合はツールに頼るのが効率的です。
JSONの書き方に不安がある場合は、JSONの書き方ガイドも参考にしてください。整形の基本テクニックについてはJSON整形の基本でまとめています。Chrome 拡張でAPIレスポンスを自動整形したい場合は、JSON整形のChrome拡張おすすめ比較も参考になります。curl で API レスポンスをデバッグ中に構文エラーに遭遇した場合は、curl レスポンスの JSON 整形方法で jq や python を使ったワンライナー整形を解説しています。

