What does "Unexpected end of JSON input" mean?

Dark navy cover card with the title 'What does "Unexpected end of JSON input" mean?' in white and a green accent bar

"Unexpected end of JSON input" means the parser reached the end of the text before it had a complete JSON value. Almost every time, the text was empty: the server sent no body, or the variable you parsed was an empty string. Less often the JSON really was cut off partway through.

Where the empty text comes from

JSON.parse('') throws this error, and so does a string that holds only spaces. In a recent Node.js release, both gave me exactly Unexpected end of JSON input. fetch gives the same thing, because Response.json() rejects with a SyntaxError when the body can't be parsed as JSON, and an empty body can't. The question is why the body was empty.

The server answered 204 No Content

A 204 No Content response means the request succeeded and there's nothing to send back. MDN is firm about it: the response must not include any content. Lots of APIs return 204 for DELETE, PUT and action endpoints.

A developer building an Atlassian Forge app called the Jira Cloud REST API v3 issue transitions endpoint to move an issue to a new status and got SyntaxError: Unexpected end of JSON input. The transition had worked. The reply on the forum pointed out that 204 means "no content", and Atlassian agreed the docs needed fixing.

In August 2015 someone opened issue #113 on the Fetch standard, "204 and json", asking for .json() to either resolve with an empty value or throw a clearer error. It still throws.

You sent a HEAD request

A HEAD request asks only for the headers. A successful response has no body, and if one shows up anyway it must be ignored. Code that reuses a generic request() helper for HEAD and then calls .json() will hit this every time.

You used mode: 'no-cors'

Setting mode: 'no-cors' makes the response opaque: its headers and body aren't available to JavaScript. People add it to silence a CORS error, and get "Unexpected end of JSON input" in its place, because the body is empty as far as your code can tell. The fix is CORS headers on the server, not no-cors on the client.

The value was never filled

Outside fetch, the usual source is a variable that holds '': an empty form field, an empty file, a cookie or environment variable that was set to nothing. JSON.parse(null) does not cause it. It returns null with no error, so a missing localStorage key won't trigger this message. An empty string stored under that key will.

What about truncated JSON?

Older guides say a response cut off mid-stream gives this error too. It depends on the engine. In my Node.js test, truncated input got its own message:

JSON.parse('{"a":1')
// Expected ',' or '}' after property value in JSON at position 6 (line 1 column 7)

JSON.parse('{"name":"Ada')
// Unterminated string in JSON at position 12 (line 1 column 13)

Firefox words these differently. The MDN list of JSON.parse errors includes JSON.parse: unexpected end of data, end of data while reading object contents and end of data when ',' or ']' was expected. In Chrome or Node, the plain "Unexpected end of JSON input" means empty. Check the body length before you hunt for a missing bracket.

How to fix it

  1. Look at the status first. If it's 204, don't parse. if (res.status === 204) return null;

  2. Read the body as text, then decide. This covers every empty case:

    const text = await res.text();
    const data = text.trim() ? JSON.parse(text) : null;
    
  3. Don't call .json() twice. A second read of the same response throws a TypeError because the body is already used. If you need the raw text for logging, read .text() once and parse that.

  4. No .json() after HEAD or no-cors. Remove mode: 'no-cors' and fix the server's CORS headers instead.

  5. Test for an empty string before JSON.parse when input comes from a file, form or storage. Empty is a normal value there.

If the text isn't empty and you still get an error, paste it into the OnlineViewer JSON validator. It shows the JSON.parse error with its position, and the parsing happens in the page.

My take

Shared fetch helpers shouldn't call res.json() directly. Read res.text(), return null for an empty body, and parse the rest. It costs one line, handles 204, HEAD and empty error replies the same way, and leaves you the raw text to log when parsing fails.

The catch: treating empty as null hides real bugs. If an endpoint that should return data suddenly sends nothing, your code carries on with null and nobody notices. For endpoints that must return a body, throw on empty instead.

FAQ

Why does response.json() throw Unexpected end of JSON input?

The response body was empty, so there was nothing to parse. A 204 No Content reply, a HEAD request and an opaque no-cors response all give you an empty body.

Does a 204 response mean the request failed?

No. 204 means the request succeeded and the server deliberately sent no body. Check the status and skip the JSON parse.

Can a cut-off JSON response cause this error?

Yes in some engines, though a recent Node.js reports a truncated object or string with a more specific message, such as Unterminated string or Expected ',' or '}', and keeps Unexpected end of JSON input for empty or whitespace-only text.

Does JSON.parse(null) throw this error?

No. JSON.parse(null) returns null without throwing, so a missing localStorage key won't trigger it. An empty string will.

Sources