Unexpected End of JSON Input: Causes and Fixes
"SyntaxError: Unexpected end of JSON input" means you tried to parse JSON from an empty or cut-off string — most often calling response.json() on an empty response like a 204 or an error. How to find the real response, handle empty bodies safely, and fix the server side.
SyntaxError: Unexpected end of JSON input
SyntaxError: JSON.parse: unexpected end of data at line 1 column 1
SyntaxError: Failed to execute 'json' on 'Response': Unexpected end of JSON input
This error means JavaScript tried to parse JSON that stopped before it was complete — and very often, there was no JSON at all: an empty string.
JSON.parse('') // Unexpected end of JSON input
JSON.parse('{"a":1') // Unexpected end of JSON input (cut off)
Its cousin, Unexpected token < in JSON, means you got HTML instead of JSON. This one means you got nothing (or half of something).
The usual culprit: response.json() on an empty response
const res = await fetch('/api/items/42', { method: 'DELETE' })
const data = await res.json() // 💥
Many responses legitimately have no body:
- 204 No Content — common for DELETE and some PUTs.
- Error responses where the server sent a status code but no body.
- Redirects and some HEAD requests.
- A server that crashed halfway through the response.
Step 1: See what you actually got
In your browser's DevTools → Network tab, click the request and check Status and Response. Or log it:
const res = await fetch(url)
const text = await res.text()
console.log(res.status, JSON.stringify(text))
An empty "" confirms the diagnosis. (Browser Developer Tools for Beginners)
Step 2: Handle empty bodies safely
async function fetchJson(url, options) {
const res = await fetch(url, options)
if (!res.ok) {
const message = await res.text()
throw new Error(`HTTP ${res.status}: ${message || res.statusText}`)
}
if (res.status === 204) return null
const text = await res.text()
return text ? JSON.parse(text) : null
}
This checks res.ok first (so error pages don't get parsed as data), treats 204 as "no data", and only parses when there's something to parse.
Step 3: Fix the server if it should have sent JSON
If the endpoint is supposed to return data:
- Make sure every code path sends the intended response. In Express,
res.json(...)sends JSON even withoutreturn; returning prevents later code from trying to send another response. A branch that ends the response without calling a JSON response method can leave the body empty. - Return JSON for errors too, like
{ "error": "Not found" }, not an empty body. - Check for crashes. If the server dies mid-response, you get a truncated body. Look at the server logs. (500 Internal Server Error)
Other places it shows up
JSON.parse(localStorage.getItem('x'))when the key doesn't exist returnsnull, which parses fine — but an empty string''doesn't. Guard it:JSON.parse(value || 'null').- Reading an empty or partially written file in Node.js, for example a config file another process is still writing.
- Webhooks where the body was already consumed by middleware, leaving an empty stream. (Handle Webhooks Reliably)
The summary
- You parsed an empty or cut-off string as JSON.
- Usually
response.json()on a 204, an error, or a crashed response. - Check
res.ok, handle 204, parse only non-empty text, and make the server always return JSON.
EasySpawn keeps your frontend, API and logs on one server, so Claude Code can see both sides of a failed request and fix whichever one is wrong. See how it works or join the waitlist.
Related: What Is JSON? · Failed to Fetch · Fetch vs Axios · JavaScript Promises
Keep reading
ReferenceError: X Is Not Defined — What It Means and How to Fix It
"Uncaught ReferenceError: x is not defined" means JavaScript can't find a variable or function with that name in the current scope. The common causes — typos, missing imports, scope, script load order, $ and jQuery, require in the browser, and window or document in server code — with a fix for each.
Module not found: Can't resolve — How to Fix It (Next.js, Vite, webpack)
"Module not found: Error: Can't resolve 'x'" means your bundler couldn't find a file or package you imported. The fixes for each cause: a package that isn't installed, wrong relative paths, case-sensitive file names that work on Mac but fail on Linux, path aliases like @/, and Node built-ins imported in browser code.