"Unexpected End of JSON Input"
This is not the same bug as "Unexpected token in JSON". That one means the parser found something it didn't expect partway through a document. This one means it ran out of document before finding anything at all — JSON.parse('') and JSON.parse('{"a":') both throw exactly this, for the same underlying reason: there's less input than a complete JSON value requires.
// where this actually comes from in practice
204 No Contentthe request succeeded and deliberately has no body — common on DELETE endpoints and some PUT/POST responses. Calling .json() on it parses an empty string500 or timeout where the server sends headers but no content — different from a server sending an HTML error page, which instead throws "Unexpected token <", not thisconst res = await fetch("/api/widgets/42", { method: "DELETE" }); const data = await res.json(); // res.json() calls JSON.parse() internally
SyntaxError: Unexpected end of JSON input
A successful DELETE conventionally returns 204 No Content -- no body at all. res.json() still tries to parse the (empty) body, which is the same as calling JSON.parse("").const res = await fetch("/api/widgets/42", { method: "DELETE" }); const text = await res.text(); // read as text first, never throws const data = text ? JSON.parse(text) : null; // only parse if something's actually there
fetch() call, not just DELETE: read the body as text once, check it's non-empty, then parse. This also means you keep the raw text to log when something does go wrong, instead of losing it the moment a failed .json() call throws.// frequently asked questions
What does "Unexpected end of JSON input" actually mean?
The parser ran out of document before finding anything at all, unlike "Unexpected token" which means it found something it didn't expect partway through. JSON.parse('') and JSON.parse('{"a":') both throw this for the same reason: there's less input than a complete JSON value requires.
Why does res.json() throw "Unexpected end of JSON input" after a fetch() call?
Almost nobody hits this by calling JSON.parse() on an empty string directly — it shows up through fetch(), where response.json() calls JSON.parse() internally on whatever the body turned out to be. A 204 No Content response (common on DELETE endpoints), a network failure mid-response, or an endpoint that sends headers but no content on error all produce this.
How do I safely parse a fetch() response that might be empty?
Read the body as text first, check it's non-empty, then parse: const text = await res.text(); const data = text ? JSON.parse(text) : null;. This never throws on an empty body, and it also keeps the raw text around to log if something does go wrong.
// try it yourself
Paste a response body into the JSON Formatter to see exactly what's actually in it before deciding how to handle the empty/partial case.
Almost nobody hits this by calling
JSON.parse()directly on an empty string on purpose — it shows up throughfetch(), whereresponse.json()callsJSON.parse()internally on whatever the body turned out to be.