---
type: errors
language: javascript
slug: unexpected-end-of-json
title: "SyntaxError: Unexpected end of JSON input"
title_tag: "Unexpected end of JSON input の原因と直し方"
summary: >
  JSON.parse に渡した文字列が途中で終わっているときに出ます。空の応答、204、途中で切れた本文、
  二重に読んだ本文。原因を多い順に並べ、落ちない受け取り方まで実行して確かめます。
description: >
  ほとんどの原因は「本文が空」です。204 や 304、body を二度読んだ場合も空になります。
  res.json() を使わず、先に文字列で受けてから判断する安全な形まで実行して示します。
status: published
difficulty: 2
minutes: 8

versions:
  verified: "Node 22.22.3"
  since: "ES5"
  deprecated: null
  removed: null

sources:
  - title: "JSON.parse — ECMAScript® 2026 Language Specification"
    url: "https://tc39.es/ecma262/#sec-json.parse"
  - title: "RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format"
    url: "https://www.rfc-editor.org/rfc/rfc8259"
  - title: "SyntaxError: JSON.parse: bad parsing — MDN"
    url: "https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Errors/JSON_bad_parse"

terms: [JSON, パース, 空の応答, ストリーム]

links:
  related:
    - javascript/reference/json/parse
    - javascript/errors/unexpected-token
    - javascript/errors/cannot-read-properties-of-undefined

content_updated_at: 2026-09-08
published_at: 2026-09-08
---

渡した文字列が**途中で終わっている**ときに出ます。

```js bad
JSON.parse('');
```
```output
SyntaxError: Unexpected end of JSON input
```

**いちばん多い原因は「本文が空だった」**です。壊れた JSON ではありません。

## 何が「途中で終わっている」なのか

```js run
for (const text of ['', '{"a":', '[1,', '{']) {
  try {
    JSON.parse(text);
  } catch (error) {
    console.log(JSON.stringify(text), '->', error.message);
  }
}
```
```output
"" -> Unexpected end of JSON input
"{\"a\":" -> Unexpected end of JSON input
"[1," -> Unexpected end of JSON input
"{" -> Expected property name or '}' in JSON at position 1 (line 1 column 2)
```

**空・値の途中・要素の途中**が、この文言になります。
`{` だけのときは別の文言です（次に何が来るべきか分かっているため）。

[dim:文言は処理系と版によって変わります。上は Node 22.22.3 のものです。]

## 原因1: 応答の本文が空

**これが大半です。** 通信の相手が何も返していません。

| 状況 | 本文 |
|---|---|
| `204 No Content` | **空**（仕様どおり） |
| `304 Not Modified` | **空** |
| エラーで本文を返さない実装 | 空 |
| `DELETE` の応答 | 空のことが多い |

`res.json()` は中で `JSON.parse()` を呼ぶので、**空だとここで落ちます。**

### 落ちない受け取り方

先に[key:文字列で受けてから]判断します。

```js run
function parseBody(status, text) {
  if (status === 204 || status === 304 || text.trim() === '') {
    return null;
  }

  try {
    return JSON.parse(text);
  } catch {
    return { error: '本文が JSON ではありません', head: text.slice(0, 40) };
  }
}

console.log(parseBody(204, ''));
console.log(parseBody(200, ''));
console.log(parseBody(200, '{"ok":true}'));
console.log(parseBody(200, '<!DOCTYPE html>'));
```
```output
null
null
{ ok: true }
{ error: '本文が JSON ではありません', head: '<!DOCTYPE html>' }
```

**`res.json()` を直接呼ばず、`res.text()` で受けてから決める**のが確実です。
状態コードも一緒に見れば、「空が正しい場合」と「異常」を区別できます。

## 原因2: 本文を二度読んだ

応答の本文は[key:一度しか読めません]。二度目は空になります。

```js bad
const body = await res.text();
const data = await res.json();   // ← ここで落ちる
```

読むのは一度だけにして、文字列から組み立ててください。

```js
const text = await res.text();
const data = text ? JSON.parse(text) : null;
```

[dim:この例は通信を伴うので、このページでは実行できません。形だけ示しています。]

## 原因3: JSON ではないものが返ってきた

サーバーがエラー画面（HTML）を返していることがあります。

```js bad
JSON.parse('<!DOCTYPE html>');
```
```output
SyntaxError: Unexpected token '<', "<!DOCTYPE html>" is not valid JSON
```

**この場合は文言が変わります。** `<` で始まっていたら、
JSON の問題ではなく[bad:通信の問題]です。
→ [SyntaxError: Unexpected token](/ja/javascript/errors/unexpected-token/)

先頭の数十文字を見れば、たいてい原因が分かります。

```js run
function diagnose(text) {
  const head = text.trim().slice(0, 20);

  if (head === '') return '本文が空';
  if (head.startsWith('<')) return 'HTML が返っている（通信を疑う）';
  if (head.includes("'")) return '単引用符がある（JSON では使えない）';

  return '中身を確かめる: ' + head;
}

console.log(diagnose(''));
console.log(diagnose('<!DOCTYPE html>'));
console.log(diagnose("{'a':1}"));
console.log(diagnose('{"a":1}'));
```
```output
本文が空
HTML が返っている（通信を疑う）
単引用符がある（JSON では使えない）
中身を確かめる: {"a":1}
```

## 途中で欠けても、この文言とは限らない

**ここは誤解しやすいところです。** 欠け方によって文言が変わります。

```js run
for (const text of ['{"name":"あ', '{"name":"あ"', '{"a":1,', '{"a":1']) {
  try {
    JSON.parse(text);
  } catch (error) {
    console.log(JSON.stringify(text), '->', error.message);
  }
}
```
```output
"{\"name\":\"あ" -> Unterminated string in JSON at position 10 (line 1 column 11)
"{\"name\":\"あ\"" -> Expected ',' or '}' after property value in JSON at position 11 (line 1 column 12)
"{\"a\":1," -> Expected double-quoted property name in JSON at position 7 (line 1 column 8)
"{\"a\":1" -> Expected ',' or '}' after property value in JSON at position 6 (line 1 column 7)
```

**どれも「途中で終わっている」のに、`Unexpected end of JSON input` は出ません。**

`Unexpected end of JSON input` が出るのは、
[key:値が始まる前に終わった]ときだけです。つまり**空か、それに近い形**。

だからこの文言を見たら、**まず「本文が空ではないか」を疑ってください。**
形が崩れているなら、もっと具体的な文言が出ます。

[dim:文言の詳しさは処理系と版によります。古い版ではもっと大雑把に `Unexpected end of JSON input` になることがあります。]

## 手で組み立てない

文字列を繋いで JSON を作ると、引用符と閉じ括弧を必ず間違えます。
**組み立てには `JSON.stringify()` を使ってください。**

```js run
const name = 'あ"い';

console.log(JSON.stringify({ name }));
console.log(JSON.parse(JSON.stringify({ name })));
```
```output
{"name":"あ\"い"}
{ name: 'あ"い' }
```

## 空白だけでも同じ

```js run
for (const text of ['   ', '\n', '\t']) {
  try {
    JSON.parse(text);
  } catch (error) {
    console.log(JSON.stringify(text), '->', error.message);
  }
}
```
```output
"   " -> Unexpected end of JSON input
"\n" -> Unexpected end of JSON input
"\t" -> Unexpected end of JSON input
```

**改行だけの応答も「空」と同じ**です。
判定するときは `text === ''` ではなく `text.trim() === ''` で見てください。

## 文字列以外を渡すと、落ちないことがある

```js run
for (const value of [null, undefined, 0, {}]) {
  try {
    console.log(JSON.stringify(value), '->', JSON.stringify(JSON.parse(value)));
  } catch (error) {
    console.log(JSON.stringify(value), '->', error.name);
  }
}
```
```output
null -> null
undefined -> SyntaxError
0 -> 0
{} -> SyntaxError
```

`JSON.parse()` は渡されたものを**先に文字列にします。**

- `null` → `'null'` は正しい JSON なので[bad:そのまま通ります]
- `0` → `'0'` も正しい JSON。通ります
- `undefined` → `'undefined'` は JSON ではないので落ちます

**`JSON.parse(null)` が `null` を返すのは、いちばん気づきにくい形です。**
値が取れなかったのか、本当に `null` だったのかが区別できません。
[em:渡す前に、文字列かどうかを確かめてください。]

## 似ているエラーとの違い

| 文言 | 意味 |
|---|---|
| `Unexpected end of JSON input` | **途中で終わっている**（空・切れている） |
| `Unexpected token '<' ...` | JSON でないものが来ている（HTML など） |
| `Expected property name or '}'` | 形が違う（単引用符・末尾のカンマ） |
| `Cannot read properties of undefined` | 読めてはいる。**中身が想定と違う** |

## 探しかたの順序

1. **`JSON.parse()` に渡している文字列を、そのまま出す。** ここで大半が分かる
2. 空なら → **応答の状態コード**を見る（204 / 304 なら正しい動き）
3. 空でないのに落ちるなら → **先頭20文字**を見る（`<` なら通信を疑う）
4. どれでもなければ → 本文を[key:二度読んでいないか]見る

[dim:このページの実行結果は Node 22.22.3 のものです。文言は処理系と版によって変わります。]

## まとめ

- ほとんどの原因は[key:本文が空]。壊れた JSON ではない
- `204` / `304` は**空が正しい**。状態コードを先に見る
- **`res.json()` を直接呼ばない。** `res.text()` で受けてから判断する
- 本文は[bad:一度しか読めない]。二度目は空になる
- `<` で始まっていたら HTML。通信の問題
- 組み立てには `JSON.stringify()` を使う

JSON そのものの決まり（何が消え、何が例外になるか）はこちらです。
→ [JSON.parse() と JSON.stringify()](/ja/javascript/reference/json/parse/)
