"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.
reference guide JavaScript fetch API
// where this actually comes from in practice
the three scenarios that actually produce this
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 stringA network failure mid-responsethe connection dropped before the body finished arriving — the parser has a legitimate JSON prefix and then nothing, which is exactly "ran out of input"
An endpoint that just doesn't return a body on errora
500 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 thiswhat actually happens, step by step
the code
const res = await fetch("/api/widgets/42", { method: "DELETE" });
const data = await res.json(); // res.json() calls JSON.parse() internallywhat happens
SyntaxError: Unexpected end of JSON input
why
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("").the fix
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 thereThe general pattern for any
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.// 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.