Fix Unexpected End of JSON Input

SyntaxError: Unexpected end of JSON input means JSON.parse() reached the end of the string while still expecting more. The parser was mid-structure, inside an open brace, an open array, or a half-finished value, and ran out of characters before the input closed. This is different from Unexpected token, where the parser hits a character it cannot make sense of. Here the problem is not a bad character; it is a missing one. The input stopped too soon.

JSON.parse() follows the JSON grammar defined in RFC 8259. An empty string, a string containing only whitespace, or any input that terminates right where a value was expected are all invalid JSON and throw this exact error. The fastest way to diagnose it is to look at what you actually passed in, not at your parsing code. Work through the three branches below in order.

Branch 1: Is the string empty? (the most common cause)

By far the leading source of this error is parsing an empty string. The clearest reproductions are the simplest ones, all confirmed in V8 (Chrome and Node.js):

JSON.parse('');      // SyntaxError: Unexpected end of JSON input
JSON.parse('   ');   // same error, whitespace only
JSON.parse('[');     // same error, opened a structure and stopped
JSON.parse(null);    // null is coerced to the string "null" and parses fine

In real code this almost always comes from a fetch response with no body. When a server returns 204 No Content or an empty 200, the body is an empty string. Calling .json() on it tries to parse '' and throws:

const res = await fetch('/api/save', { method: 'POST' });
const data = await res.json(); // throws on a 204 or empty body

The fix is to check the response before parsing. Read the body as text first, and only parse if there is something to parse:

const res = await fetch('/api/save', { method: 'POST' });
const text = await res.text();
const data = text ? JSON.parse(text) : null;

Other empty-string sources worth checking: localStorage.getItem(key) returns null for a missing key but an empty string if you stored one, a database column that is NULL or '', an environment variable that was never set, or a file that was created but never written. Log the raw value with JSON.stringify(raw) right before the parse call so you can see the quotes around an empty string instead of guessing.

Branch 2: Is the string truncated mid-stream?

If the value is not empty but still throws, it may be cut off partway through. Truncation produces valid-looking JSON that simply stops, for example {"name":"Ada","items":[1,2. The parser consumes everything correctly and then hits the end while still inside the array. Modern engines usually report this with a position-aware message such as Expected ',' or ']' after array element rather than the bare end-of-input text, but the underlying cause is the same: the data ran out early.

Common causes of truncation:

  • A network request that was aborted, timed out, or had its connection dropped before the full payload arrived.
  • A response read twice. A fetch body is a one-shot stream; calling .json() after you already called .text() (or vice versa) throws because the stream is already consumed.
  • A buffer or string-length limit on the server that silently chops large responses.
  • A streaming or chunked response where you parsed a single chunk instead of waiting for the whole body to assemble.
  • A file copied or downloaded incompletely, so the last bytes are missing.

To confirm truncation, compare the byte length you received against the Content-Length header, or log the last 40 characters of the string. If it ends in the middle of a value rather than with a closing } or ], the data was cut off and your parser is fine. The bug is upstream in whatever produced or transmitted the JSON.

Branch 3: Unterminated bracket or trailing comma

JSON is stricter than JavaScript object literals. An open structure that never closes is the classic end-of-input trigger:

JSON.parse('{"a":');   // SyntaxError: Unexpected end of JSON input (value expected, none found)
JSON.parse('[');       // SyntaxError: Unexpected end of JSON input (array never closed)

Trailing commas are a separate problem. RFC 8259 does not allow them, even though the same syntax is legal in a JavaScript object literal. A dangling comma is not an end-of-input case, though, because there is still a closing bracket after it. Current V8 reports it with a more specific message:

JSON.parse('{"a":1,}'); // SyntaxError: Expected double-quoted property name in JSON
JSON.parse('[1,2,]');   // SyntaxError: Unexpected token ']' in JSON

The practical rule: Unexpected end of JSON input points at input that stops where a value was expected, while a stray comma or a missing comma between items reports a token error instead. Either way, if you are hand-building JSON by concatenating strings, a loop that forgets to write the closing bracket or string interpolation that drops the final character is the usual culprit. Stop building JSON by hand and use JSON.stringify() instead; it always produces well-formed output.

Every branch ends the same way: see exactly where it cuts off

Whichever branch you are on, the decisive step is to look at the raw string. Paste it into our JSON formatter, which parses entirely in your browser and points to the precise position where the structure breaks down, so you can see whether it is empty, truncated, or missing a bracket. If you have a known-good version of the payload, drop both into the diff checker to spot exactly which characters went missing. For the related case where a stray character (not a missing one) is the problem, read our companion guide on the Unexpected token error.

A copy-paste guard for production code

Once you know the cause, wrap parsing so a bad input degrades gracefully instead of crashing. A try/catch with a small validation helper covers all three branches:

function isValidJSON(str) {
  if (typeof str !== 'string' || str.trim() === '') return false;
  try {
    JSON.parse(str);
    return true;
  } catch {
    return false;
  }
}

function safeParse(str, fallback = null) {
  if (!isValidJSON(str)) return fallback;
  return JSON.parse(str);
}

For fetch specifically, guard the body and check the status before you ever call .json():

async function fetchJSON(url, options) {
  const res = await fetch(url, options);
  if (res.status === 204 || res.status === 304) return null;
  const text = await res.text();
  if (!text) return null;
  try {
    return JSON.parse(text);
  } catch (err) {
    throw new Error(`Invalid JSON from ${url}: ${err.message}`);
  }
}

This pattern fixes the empty-body case outright and turns a cryptic parse crash into a clear, sourced error message for truncated or malformed responses. The error itself is never random: the string ended before the JSON did. Find out why it ended early, and the fix follows directly.

Frequently Asked Questions

It means JSON.parse() reached the end of the input string while still expecting more characters. The parser was inside an open object, array, or value and the string ran out before the closing token. The input ended too soon rather than containing a wrong character.

A 204 No Content or any empty-body response gives res.json() an empty string to parse, which is invalid JSON. Read the body with res.text() first and only call JSON.parse() if the text is non-empty, or return null for status 204 and 304 before you ever call .json().

Unexpected token means the parser hit a character it cannot accept, such as HTML or a stray symbol. Unexpected end of input means a character is missing, the input is empty or stops where a value was expected. One is a bad character; the other is a missing one, so the causes and fixes differ.

No. RFC 8259 forbids trailing commas, so {"a":1,} is invalid even though the same syntax is legal in a JavaScript object literal. On current V8 a trailing comma reports a token error (not end of input), since a closing bracket still follows it. Use JSON.stringify() to avoid the problem.

Paste the raw string into a JSON formatter that validates in the browser; it points to the exact position where parsing fails. Check whether the string ends mid-value instead of with a closing brace or bracket, which confirms the data was truncated upstream rather than a parsing bug.