Unexpected end of JSON input: Causes and Fixes

Unexpected end of JSON input is one of the most common JavaScript errors, and it is also one of the most misunderstood. It does not mean your JSON has a typo in the middle. It means the parser reached the end of the text before the JSON was complete, so it was still waiting for more. This guide explains what actually triggers it and how to fix each cause.

What the error really means

JSON.parse reads text character by character and builds a value. If it opens an object with { but never sees the closing }, or if it is handed an empty string, it runs off the end of the input while still expecting more. That is the “unexpected end”: the text stopped too soon.

The single most important clue is that the problem is almost always at the end of the data, or the data is empty, not a stray character in the middle. If your error were a bad character mid-string, you would get Unexpected token instead. Knowing that difference narrows the search immediately. For the sibling error, see the guide on fixing Unexpected token errors.

Cause 1: parsing an empty string

This is by far the most common trigger. JSON.parse("") throws Unexpected end of JSON input because an empty string is not valid JSON, it is nothing at all.

JSON.parse("");            // Unexpected end of JSON input
const v = localStorage.getItem("missingKey");  // returns null
JSON.parse(v);             // null coerces to "null" and parses to null, but an
                           // empty stored value "" throws Unexpected end of JSON input

The fix is to guard before you parse:

const raw = localStorage.getItem("cart");
const cart = raw ? JSON.parse(raw) : [];

Any source that can legitimately be empty, localStorage, a form field, a cache, a database column, needs this check. Provide a sensible default instead of parsing blindly.

Cause 2: an empty or non-JSON HTTP response

When you parse a network response, the body may be empty or may not be JSON at all. A 204 No Content reply, a failed request, or an endpoint that returns an empty body on error all produce this exact message.

const res = await fetch("/api/data");
// If the server returned 204 or an empty body, res.json() throws here
const data = await res.json();

Fix it by checking the response before parsing:

const res = await fetch("/api/data");
if (!res.ok) throw new Error(`HTTP ${res.status}`);

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

Reading the body with .text() first lets you decide what to do when it is empty, instead of letting .json() throw. If the body is supposed to be JSON but is not, logging the raw text usually reveals an HTML error page or a proxy message. The deeper mechanics are in how to parse JSON in JavaScript.

Cause 3: truncated or incomplete data

If the JSON was cut off partway, the closing brackets never arrive and the parser hits the end early. This happens with interrupted downloads, a response that exceeded a size limit, a log line that was clipped, or a file that was not fully written before being read.

{"users": [{"id": 1, "name": "Ada"}, {"id": 2, "nam

That fragment is valid up to the point it stops, which is exactly why the error is “unexpected end”. The fix is upstream: make sure the full payload is delivered. Check for a Content-Length mismatch, remove any truncation in logging or storage, and confirm the writer flushed and closed the file. To see where a fragment breaks, paste it into the JSON parser or open it in the JSON viewer, which shows how far the structure parses before it runs out.

Cause 4: double parsing

If something already parsed the JSON for you, the result is an object. Parsing it again stringifies the object first, and in some runtimes an already-consumed stream reads as empty, producing this error. The rule is simple: parse once. fetch‘s .json(), the axios library, and many SDKs return parsed objects already, so calling JSON.parse on their output is both unnecessary and a source of errors.

Cause 5: concatenated or trailing content gone wrong

Building JSON by hand with string concatenation is error-prone. Forgetting to append the closing bracket, or building an array in a loop and never closing it, leaves the parser waiting. Whenever you can, build a JavaScript object or array and call JSON.stringify at the end rather than assembling JSON text yourself. If you must inspect hand-built output, run it through a JSON formatter; a formatter fails loudly on an unterminated structure, making the missing bracket obvious.

A quick debugging checklist

  1. Log the raw input right before parsing. Is it empty? Is it HTML? Is it cut off at the end?
  2. If it is empty, add a guard and a default value.
  3. If it is from fetch, check res.ok and read with .text() first.
  4. If it looks complete but still fails, check for a missing closing } or ] with a JSON parser.
  5. If it is already an object, stop calling JSON.parse on it.

Frequently asked questions

What causes “Unexpected end of JSON input”?
The parser reached the end of the text before the JSON was complete. The usual causes are an empty string, an empty or non-JSON HTTP response, truncated data, or double parsing something that was already an object.

Why does JSON.parse fail on an empty string?
An empty string contains no JSON value, so there is nothing to parse. JSON.parse("") throws immediately. Guard with raw ? JSON.parse(raw) : defaultValue before parsing anything that can be empty.

How do I fix it with fetch?
Check res.ok, then read the body with await res.text() and only parse if the text is non-empty. A 204 No Content or an error response often has an empty body, which makes res.json() throw.

Is this the same as “Unexpected token in JSON”?
No. “Unexpected end” means the text stopped too early. “Unexpected token” means there is an invalid character somewhere in the middle. See the guide on Unexpected token errors for that one.

For the rest of the toolkit, including validating and formatting your data, see the roundup of free JSON tools, or learn the format fundamentals in what JSON is.

Leave a Reply

Your email address will not be published. Required fields are marked *