← 記事一覧

Unexpected End of JSON Input: 起こる理由と直し方

パーサが構造完了の前に文字列の終わりに到達。原因は、切り詰められた API 応答、閉じていない括弧、空文字列まで多様。5 パターン、5 つの修正。

SyntaxError: Unexpected end of JSON input は、JSON 構造が完成する前にパーサが文字列の終わりに達した、という意味です。データが途切れています。本記事はこれが起きる一般的なすべての例と、それぞれの正確な直し方を示します。

このエラーの意味

JSON.parse() は状態機械です。1 文字ずつ読み、開いた括弧、ブレース、文字列の区切りを追跡します。入力の末尾に到達した時点でまだ「何かの中」 —— オブジェクト、配列、文字列 —— にいると、こう投げます:

SyntaxError: Unexpected end of JSON input

このエラーは行番号や位置を示しません。問題が内容の不在 そのものだからで、指し示せるものがないのです。

原因 1 —— API レスポンスの切り詰め

実世界で最も多い原因。サーバが大きな JSON ボディを送ったが、ネットワークが早く切断された、レスポンスが完了する前に読まれた、あるいはプロキシ/CDN のバッファ上限に達した、など。

// 届いたもの(途中で接続が切れた):
'{"users":[{"id":1,"name":"Ada"},{"id":2,"na'

JSON.parse('{"users":[{"id":1,...') // SyntaxError: Unexpected end of JSON input

修正: HTTP ステータスコードと Content-Length ヘッダを確認。parse 前に生のレスポンス長をログに残す。fetch を使うなら、response.text() を読んで手動で parse するのではなく、必ず response.json() を await してください —— ブラウザ組み込みの JSON パーサは切り詰めについてより明確なエラーを出します。

// 正しい
const data = await response.json();

// 脆い —— 切り詰めエラーを隠す
const text = await response.text();
const data = JSON.parse(text);

原因 2 —— 閉じていないオブジェクトや配列

JSON が手書き、またはコード生成で閉じ括弧/ブレースを忘れた場合。

JSON.parse('{"name":"Ada","plan":"pro"')
// 閉じる } がない
// SyntaxError: Unexpected end of JSON input

JSON.parse('[1, 2, 3')
// 閉じる ] がない
// SyntaxError: Unexpected end of JSON input

修正: JSON リンターを使うか、文字列を JSON Fix に貼り付ける —— 閉じていない区切りを特定し、寛容モードでは自動的に補えます。

原因 3 —— 閉じていない文字列

値の文字列が閉じのダブルクォートで終わらない —— しばしば値自体にエスケープされていないダブルクォートが含まれることで起きます:

JSON.parse('{"title":"He said "hello" to her"}')
//                              ^ エスケープなしの引用符が文字列を早く閉じる
// SyntaxError: Unexpected end of JSON input

// 正しい —— 内側の引用符をバックスラッシュでエスケープ:
JSON.parse('{"title":"He said \"hello\" to her"}')

修正: 内側のダブルクォートを \" にエスケープするか、最初から JSON.stringify() を使って JSON を構築し、文字列を手で組み立てるのをやめます。

原因 4 —— 空文字列

空文字列や空白のみの文字列を JSON.parse() に渡しても同じエラーが投げられます —— parse すべき入力がそもそもありません。

JSON.parse('')   // SyntaxError: Unexpected end of JSON input
JSON.parse('  ') // SyntaxError: Unexpected end of JSON input
JSON.parse()     // SyntaxError: Unexpected token u…(undefined が文字列化される)

フォームフィールド、localStorage のキー、環境変数が空のときによく起きます。

// parse 前にガード
const raw = localStorage.getItem('session');
if (!raw) return null;
return JSON.parse(raw);

原因 5 —— ストリーミングレスポンスを早く読みすぎ

ストリーミング API や WebSocket を消費していると、メッセージ全体が届く前にチャンクを parse しようとしてしまいがちです。

// ❌ 不完全なチャンクを parse しようとする
ws.onmessage = (event) => {
  const data = JSON.parse(event.data); // 部分的かもしれない
};

// ✓ メッセージ境界までバッファ(アプリ層のフレーミング)
let buffer = '';
ws.onmessage = (event) => {
  buffer += event.data;
  if (buffer.endsWith('\n')) {        // または既知の区切り
    const data = JSON.parse(buffer.trim());
    buffer = '';
  }
};

切り詰めの診断:Content-Length とストリーミングリーダー

レスポンスが本当に短く切られたかを確認する 2 つの具体的な手法:

  • Content-Length と比較。 サーバが設定しているなら短読を直接検出できます:
    const res = await fetch(url);
    const expected = Number(res.headers.get('Content-Length') ?? NaN);
    const text = await res.text();
    if (Number.isFinite(expected) && text.length !== expected) {
      throw new Error(`truncated: got ${text.length} of ${expected} bytes`);
    }

    注意:Content-Length はチャンク化/圧縮レスポンスでは存在しないことがあります。

  • ストリーミングボディリーダーで読む。 res.body.getReader() を使うと、チャンクごとの到着を観察し、ストリームがペイロードの途中で閉じたのに気づけます —— 接続切れやプロキシタイムアウトの症状です。ブラウザはストリームが中断されると fetch の reject として基底のネットワーク障害を表面化させます;パーサのエラーと突き合わせて確認しましょう。

クイック診断チェックリスト

  • parse 呼び出し直前に typeof inputinput.length をログ。
  • 長さが短すぎるならレスポンスが切り詰められた —— ネットワークログを確認。
  • 長さが 0 ならソース(API、ストレージ、env)が何も返していない —— ガードを追加。
  • 長さが正しそうなら、文字列を JSON Fix に貼り付けて構造の問題を見つけてください。

本番でグレースフルにリカバ

function safeParse(text, fallback = null) {
  if (!text || !text.trim()) return fallback;
  try {
    return JSON.parse(text);
  } catch {
    console.error('JSON parse failed, input length:', text.length);
    return fallback;
  }
}

よくある質問

「Unexpected end of JSON input」の原因は?

パーサがオブジェクト・配列・文字列の中にいる状態で文字列の末尾に達した —— データが不完全。最も多い実世界の原因は API レスポンスの切り詰め;他には未閉括弧、未終了文字列、空文字列。

なぜ行番号や位置がない?

問題が文字の間違いではなく内容の不在だから。指し示すものがなく、構造が閉じる前に入力を使い切ったのです。

空文字列の場合はどう直す?

parse 前にガード:if (!raw || !raw.trim()) return fallback;。空のフォームフィールド、欠落した localStorage キー、未設定の環境変数が定番。

本番でグレースフルに扱うには?

JSON.parse()safeParse ヘルパで包み、失敗時にフォールバックを返す(上のスニペット参照)、入力長をログして切り詰めと構造不良を区別できるようにする。パターン全体は JavaScript で壊れた JSON を扱う

ブラウザで切り詰められた JSON を修復

検査・修復したい壊れた JSON 文字列がありますか? JSON Fix は fixjson.org で parse、検証、よくある構造エラー(欠落括弧を含む)の自動修復を、ブラウザ内で完結して行います。データはどのサーバにも送信されません。