JSON Debugging

Fix "Unexpected End of JSON Input" Error in JavaScript

The SyntaxError: Unexpected end of JSON input error means your code tried to parse a string that looks like JSON but is incomplete. Here are the 6 most common causes and how to fix each one.

Quick Fix
  1. 1. Check if the string is empty before parsing: if (!str) return null;
  2. 2. Always check response.ok before calling response.json()
  3. 3. Wrap JSON.parse in try/catch — never let it crash your app

What the Error Means

JSON.parse() reads a string from start to end, expecting a complete JSON value. When it reaches the end of the string before finding a matching closing bracket, brace, or quote, it throws SyntaxError: Unexpected end of JSON input.

// These all cause the error:
JSON.parse('');               // empty string
JSON.parse('{"name"');        // missing closing brace
JSON.parse('[1, 2, 3');       // missing closing bracket
JSON.parse('"hello');         // unclosed string

6 Common Causes and Fixes

Cause 1: Empty String

The most common cause — passing an empty string to JSON.parse()

// Common in fetch when server returns empty body
const response = await fetch('/api/data');
const data = await response.json();  // ← throws if body is empty

// Also with localStorage
const raw = localStorage.getItem('key');  // returns "" or null
JSON.parse(raw);  // ← throws if raw is ""
// Fix: check before parsing
const raw = localStorage.getItem('key');
const data = raw ? JSON.parse(raw) : null;

// Fix with fetch: check response.ok and content-length
const response = await fetch('/api/data');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const text = await response.text();
const data = text ? JSON.parse(text) : null;

Cause 2: Truncated API Response

The server sent an incomplete JSON body due to a crash, timeout, or proxy issue

// Server crashed mid-response, sending only:
// {"users": [{"id": 1, "name": "Al

// Your code:
const data = await response.json();
// ← SyntaxError: Unexpected end of JSON input
// Fix: read as text first, validate, then parse
const response = await fetch('/api/data');
const text = await response.text();

try {
  const data = JSON.parse(text);
} catch (e) {
  console.error('Invalid JSON response:', text.slice(0, 200));
  throw new Error('Server returned invalid JSON');
}

Debug tip: Log the raw response text to see where it was cut off. This often reveals a server-side error (PHP memory limit, Nginx buffer overflow, etc.).

Cause 3: Missing Closing Bracket or Brace

A manually constructed JSON string with a missing closing brace or bracket

INVALID

{
  "name": "Alice",
  "items": [1, 2, 3
}

VALID

{
  "name": "Alice",
  "items": [1, 2, 3]
}

Fix: Use JSON.stringify() to build JSON strings instead of manual concatenation. It always produces valid JSON.

Cause 4: Unclosed String Literal

A string value that is missing its closing double quote

INVALID

{"message": "Hello world}

VALID

{"message": "Hello world"}

Common scenario: A string contains an unescaped double quote or newline character that terminates the string early. Use JSON.stringify() to properly escape values.

Cause 5: Network Interruption

Connection dropped mid-transfer, leaving an incomplete JSON body

// Fix: use AbortController with timeout
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000);

try {
  const response = await fetch('/api/data', {
    signal: controller.signal,
  });
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  const data = await response.json();
} catch (e) {
  if (e.name === 'AbortError') {
    console.error('Request timed out');
  } else if (e instanceof SyntaxError) {
    console.error('Invalid JSON — possible network truncation');
  }
} finally {
  clearTimeout(timeout);
}

Cause 6: HTML Instead of JSON

Server returned an HTML error page, but your code assumes JSON

// Server returned a 404 HTML page:
// <!DOCTYPE html><html>...

// Your code blindly parses it:
const data = await response.json();
// ← The "<!DOCTYPE" starts like a string but never closes
//    → "Unexpected end of JSON input"
// Fix: check Content-Type header
const response = await fetch('/api/data');
const contentType = response.headers.get('content-type');

if (!response.ok || !contentType?.includes('application/json')) {
  const text = await response.text();
  throw new Error(`Expected JSON, got: ${text.slice(0, 100)}`);
}
const data = await response.json();

Safe JSON.parse Utility Function

Use this utility in your projects to never crash on bad JSON again:

/**
 * Safely parse JSON without throwing.
 * Returns fallback (null by default) on any error.
 */
function safeJsonParse<T = unknown>(
  str: string | null | undefined,
  fallback: T | null = null,
): T | null {
  if (!str || typeof str !== 'string') return fallback;
  try {
    return JSON.parse(str) as T;
  } catch {
    return fallback;
  }
}

// Usage examples:
const data = safeJsonParse(localStorage.getItem('user'));
// data is null if missing or invalid — no crash

const config = safeJsonParse<Config>(raw, defaultConfig);
// config falls back to defaultConfig if parse fails

Prevention Best Practices

Always check response.ok before calling response.json()
A 404 or 500 response returns HTML, not JSON. Checking response.ok prevents the most common cause of this error.
Never pass user input directly to JSON.parse()
Validate that the string is non-empty and looks like JSON (starts with {, [, or ") before parsing.
Use JSON.stringify() to build JSON, not string concatenation
Manual JSON construction is error-prone. JSON.stringify() always produces valid JSON and handles escaping automatically.
Wrap JSON.parse() in try/catch in production code
Never let a parse error crash your application. Always provide a fallback value and log the error for debugging.
Validate API responses with a schema
Use Zod, Joi, or JSON Schema to validate the structure of parsed data. This catches missing fields and type mismatches early.
Check Content-Type: application/json on responses
If the Content-Type header is text/html, do not attempt to parse as JSON. This prevents the HTML-instead-of-JSON scenario.

Frequently Asked Questions

What does "Unexpected end of JSON input" mean?

It means JSON.parse() reached the end of the string before finding a complete JSON value. The string started like valid JSON (e.g., an opening brace or bracket) but was cut off mid-structure. Common causes: empty string, truncated API response, missing closing bracket, or unclosed string literal.

How do I fix "Unexpected end of JSON input" in fetch?

Always check response.ok and the Content-Type header before calling response.json(). If the server returns an empty body or HTML error page, response.json() will throw this error. Pattern: if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json();

Why does JSON.parse("") throw "Unexpected end of JSON input"?

An empty string is not valid JSON. JSON.parse() expects at least one complete value (object, array, string, number, boolean, or null). An empty string gives the parser nothing to work with, so it reports "unexpected end". To handle this, check if the string is empty before parsing: const data = str ? JSON.parse(str) : null;

Is "Unexpected end of JSON input" the same as "Unexpected token"?

No. "Unexpected end of JSON input" means the string ended too early — the parser was expecting more content. "Unexpected token" means the parser found a character it did not expect at that position (e.g., a trailing comma, a single quote, or an HTML "<" character). Both are SyntaxError types from JSON.parse().

Key Takeaways

  • "Unexpected end of JSON input" means the JSON string was incomplete — it started but never finished.
  • Empty strings and truncated API responses are the two most common causes.
  • Always check response.ok before calling response.json().
  • Use a safeJsonParse() utility with try/catch and a fallback value.
  • Use our JSON Repair tool to auto-fix broken JSON, or JSON Validator to find the exact error location.

Related Resources