SyntaxError: Unexpected end of JSON input means the JSON parser reached the end of the string before finding a complete, valid JSON document. The JSON is cut off somewhere — a bracket never closed, the response was truncated, or you only pasted part of the data.
The four main causes
1. Truncated API response
Network timeouts, response size limits, or streaming errors can cut off an API response mid-transfer. The beginning of the JSON is valid, but the document ends abruptly without all closing braces and brackets.
// Truncated — missing the closing ] and }
{"users": [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"
To diagnose: log the raw response text before parsing. Check the final characters — if they don't end with a complete JSON value (}, ], a quoted string, a number, true, false, or null), the response was cut.
2. Unclosed brackets or braces in hand-written JSON
When editing JSON manually, it's easy to open a brace or bracket and forget to close it, especially in deeply nested structures.
// Missing closing } for the "address" object and } for the outer object
{
"name": "Alice",
"address": {
"city": "London",
"postcode": "SW1A 1AA"
The parser reads the entire string, reaches the end, and realises it's still waiting for the closing tokens it expected.
3. Parsing an empty string
A 204 No Content API response, or an endpoint that returns nothing on success, gives you an empty body. Calling JSON.parse('') throws "Unexpected end of JSON input" because an empty string is not valid JSON.
// This throws:
JSON.parse(''); // SyntaxError: Unexpected end of JSON input
// Guard against empty responses:
const text = await res.text();
const data = text ? JSON.parse(text) : null;
4. Partial file write
If a process writing a JSON file crashes or is interrupted mid-write, the file will be incomplete. Reading and parsing an incomplete JSON file produces this error. Always write JSON files atomically: write to a temporary file, then rename it to the target filename, so readers never see a partial write.
How to find the unclosed bracket
For large JSON blobs, finding the missing bracket manually is tedious. The fastest approach is to paste the JSON into the JSON Formatter. The tree view renders each nested object and array as a collapsible node. A structure that's open but never closed will be visible in the tree as an empty or incomplete branch.
You can also count brackets programmatically to identify the imbalance:
function countBrackets(json) {
let braces = 0, brackets = 0;
let inString = false;
for (let i = 0; i < json.length; i++) {
const char = json[i];
// Track strings to skip brackets inside them
if (char === '"' && json[i - 1] !== '\\') inString = !inString;
if (inString) continue;
if (char === '{') braces++;
if (char === '}') braces--;
if (char === '[') brackets++;
if (char === ']') brackets--;
}
return { unclosedBraces: braces, unclosedBrackets: brackets };
}
// Example:
countBrackets('{"name": "Alice", "scores": [1, 2');
// { unclosedBraces: 1, unclosedBrackets: 1 }
Validating before parsing
Use the JSON Validator to check any JSON before you parse it in production. The validator reports the exact line and character position of the error, which is far more useful than the generic "unexpected end of input" message from most runtimes.
Defensive parsing in production code
async function safeParseResponse(res) {
const text = await res.text();
if (!text || !text.trim()) {
return null; // Empty response — not an error, just nothing to parse
}
try {
return JSON.parse(text);
} catch (err) {
console.error('Failed to parse JSON response:', err.message);
console.error('Response body (first 500 chars):', text.slice(0, 500));
throw err;
}
}