Unexpected end of JSON input
Updated by John Marshall
You get this when JSON.parse reaches the end of the text while it’s still expecting more. Usually there was no text at all, because the response came back empty and response.json() had nothing to read. The other possibility is JSON that stops partway through, with an object or array that never gets closed. An empty response isn’t necessarily a mistake on the server’s part. A 204 No Content reply never has a body, so calling response.json() on one always fails.
Quick fix
- Check the status code in your browser’s Network tab. A 204 never has a body, and plenty of error responses don’t either.
- Read the body with
response.text(), and only parse it when there’s something in it. - If the JSON is cut off, paste it below to see where it stops, then fetch or export it again.
Fix your JSON
When the response is empty
The classic case is a 204 No Content. Lots of APIs send one after a successful DELETE or PUT, so calling response.json() on it fails every time, even though the request worked. Some servers also send a 401, 404, or 500 with nothing in the body.
Then there’s mode: "no-cors". It often gets added to make a CORS error go away, but it gives you an opaque response whose body is always empty. Take it out, and have the server send the right CORS headers instead.
In all of these cases, read the text first and only parse it if there’s something there:
const response = await fetch(`/api/items/${id}`, { method: "DELETE" });
if (!response.ok) throw new Error(`Request failed with ${response.status}`);
const text = await response.text();
const data = text ? JSON.parse(text) : null;If the API is yours, look at what the route sends back. In Express, res.end() sends an empty body even with a 200 status, and the client’s response.json() fails on it just like it does on a 204:
// Sends an empty body. response.json() on the client will fail.
res.status(200).end();
// Sends JSON the client can parse.
res.json({ deleted: true });
// Or send 204 on purpose, and have the client check for it.
res.status(204).end();Outside of fetch, the same error comes from JSON.parse(""), and from a string that contains nothing but spaces or line breaks. JSON.parse(null) behaves differently and returns null. That’s why JSON.parse(localStorage.getItem("cart")) works when the key doesn’t exist, but fails if something saved an empty string under it.
When the JSON is cut off
If the text isn’t empty, it ends before every object or array is closed. That can happen when a log viewer shortens long lines, when only part of a file gets copied, or when a proxy or server gives up partway through a large response.
Chrome won’t always call this an “unexpected end”. If the text happens to stop right after a complete value, you’ll see something like Expected ',' or ']' after array element in JSON at position 29 instead. A quick check: if the position equals text.length, the JSON was cut off.
In Node.js, a common way to cut JSON off yourself is to parse inside a stream’s data handler. A response arrives in chunks, and with a large body the first chunk is only part of it. With a 150 KB response, for example, the first chunk might be only 64 KB. Depending on where that chunk ends, you get “Unexpected end of JSON input”, “Unterminated string”, or another error. Collect the chunks and parse once the response has finished:
http.get(url, (res) => {
let body = "";
res.setEncoding("utf8");
res.on("data", (chunk) => { body += chunk; });
res.on("end", () => {
const data = JSON.parse(body);
// use data here
});
});JSON written by a language model can stop early too. When a reply hits the output token limit, the text ends wherever the model happened to be. OpenAI’s API marks this with a finish_reason of "length", and Anthropic’s with a stop_reason of "max_tokens". Check for those before parsing, and raise the limit or ask for less output.
The fixer above can close the open brackets so you can read what’s there, but anything after the cut is gone. To get the full data, fetch or export it again.
When a JSON file is empty or half-written
JSON.parse(fs.readFileSync("settings.json", "utf8")) throws the same error when the file is empty. That usually means a program crashed while saving it, or something read the file while another process was still writing to it.
If your code writes the file, save to a temporary file first and then rename it over the real one. On the same disk, a rename swaps the file in a single step, so anything reading it gets either the old version or the new one, never a partly written file:
import { writeFileSync, renameSync } from "node:fs";
writeFileSync("settings.json.tmp", JSON.stringify(settings));
renameSync("settings.json.tmp", "settings.json");The same error elsewhere
| Chrome, Edge, Node.js | Unexpected end of JSON input |
|---|---|
| Firefox | JSON.parse: unexpected end of data at line 1 column 1 of the JSON data |
| Safari | JSON Parse error: Unexpected EOF |
| Python (empty) | JSONDecodeError: Expecting value: line 1 column 1 (char 0) |
| Go | unexpected end of JSON input (json.Unmarshal) or unexpected EOF (json.Decoder) |