Short answer
Check the response before parsing it. If res.status is 204 or the body is empty, don’t call res.json(). If res.ok is false, read res.text() and log it, because error responses are often empty or not JSON. For JSON.parse, guard against empty strings, and handle null from localStorage. If the body is cut off part-way, the server crashed or timed out mid-response: check its logs.
Why the JSON ended early, and the fix
01
The response has no body
“The response body cannot be parsed as JSON.” MDN, Response: json() method, exceptions
A 204 No Content reply, a DELETE handler that ends with res.end(), or an error response with no body gives res.json() an empty string to parse. Check the status and only parse when there is content.
const res = await fetch(url, { method: 'DELETE' }); if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`); const data = res.status === 204 ? null : await res.json();02
An error status is parsed as if it succeeded
“if the server responds with an error like 404, then fetch() fulfills with a Response, so we have to check the status before we can read the response body.” MDN, Using the Fetch API
fetch() does not reject on 404 or 500, so code that calls res.json() straight away tries to parse an empty or HTML error body. Check res.ok first.
03
JSON.parse on an empty string
An empty string from localStorage, a form field or an empty environment variable throws this error. A missing key gives null, which parses without error but breaks the code after it. Parse only when there is a value, and fall back to a default.
function readJson(key, fallback) { const raw = localStorage.getItem(key); if (!raw) return fallback; try { return JSON.parse(raw); } catch { return fallback; } }04
The server sent half a response
If the body stops part-way, the server crashed, hit a timeout or ran out of memory while writing, or a proxy cut the connection. The client fix is a retry; the real fix is in the server logs at that timestamp.
Checked against the vendor’s docs on : MDN: Response json() method; MDN: Using the Fetch API; MDN: SyntaxError: JSON.parse: bad parsing.
Rather hand it over?
Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.
- from $450App Fix TicketOne reproducible bug in an existing mobile or web app, diagnosed and fixed.See scope and start
- from $450AI-Built App RescueA Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.See scope and start
Related: “Unexpected token < in JSON” · “TypeError: Failed to fetch” · “Cannot read properties of undefined”