"Unexpected end of JSON input" — The Body Was Empty or Cut Short
This error means the parser ran out of text before the JSON structure was complete. Almost always the response body was empty, or the transfer was truncated — and the two have completely different fixes.
Unlike most JSON errors, this one does not name a character, because there was no character left to name. The parser reached the end of the input while still waiting for a closing brace, bracket or quote. Your parsing code is fine — you were handed less text than a complete JSON document.
The most common cause: the body was completely empty
JSON.parse("") throws exactly this error, and an empty string is what you get more often than you would expect. A 204 No Content response has no body by definition, which is standard for a successful DELETE or PUT. A 304 Not Modified also returns nothing, because the point is that your cache is still valid. A HEAD request never returns a body. And a request that failed at the network level — connection refused, CORS preflight rejected, request aborted — can leave you with an empty string rather than an exception, depending on the client.
The second cause: the response was truncated mid-flight
Here the body starts out as valid JSON and simply stops partway through. The connection dropped, a timeout fired while the response was still streaming, or something between you and the server imposed a size limit — reverse proxies, nginx buffer settings and serverless response caps all do this silently. A useful signal is that truncation tends to be intermittent and correlates with payload size: small responses work, large ones fail. If the same endpoint parses fine for one record and breaks for a thousand, you are looking at truncation, not malformed JSON.
The third cause: reading from storage or a file
If the JSON comes from localStorage, a cache file or disk, the write may have been the incomplete half. localStorage silently fails once the quota is exceeded, so a large object can be half-written with no exception raised at write time — the error only surfaces later when something tries to read it back. A file written by a process that crashed or a disk that filled up produces the same result. The distinguishing feature is that it fails identically every single time, whereas a network truncation is usually intermittent.
How to tell them apart in one step
Before calling JSON.parse, log the raw value's length and its last few characters. A length of zero means an empty body, and you should be handling the status code rather than parsing at all. A non-zero length that does not end in } or ] means truncation, and you should compare the received length against the Content-Length header. A length that looks complete but still fails means the problem is somewhere else in the string and the message would have named a character instead.
The fix is a guard, not a try/catch
The instinct is to wrap the parse in try/catch and move on, but that hides the real condition and turns a clear error into a silent empty state. Check the status code first and handle 204 and 304 explicitly as "no body, and that is correct". Verify the response was actually successful before assuming there is anything to parse. A try/catch belongs around genuinely unexpected malformed data, not around the entirely predictable case of a response that was never supposed to contain JSON.
When you do have the raw text and want to know exactly where it stops being valid, paste it into our JSON validator — it reports the position where the structure breaks off, which makes a truncation immediately obvious. It runs in your browser, so a real API response is safe to paste.