Unexpected end of JSON input

Common sources of “Unexpected end of JSON input” and how to avoid them

If you’ve been working with JavaScript for any length of time, especially in web development where you’re fetching data from APIs or handling user inputs, you’ve probably run into the frustrating “SyntaxError: Unexpected end of JSON input” at some point. This error pops up when you’re trying to parse a string as JSON using something like JSON.parse(), but the parser hits the end of the string before it finds a complete, valid JSON structure.

It could be from a botched API response, a corrupted file, or just a simple formatting slip-up. Back in the early days of AJAX and dynamic web apps, this was a common headache as developers started relying more on JSON for data exchange—think of it as the parser expecting a full meal but getting served an empty plate.

In this guide, I’ll walk you through the most common sources of this error based on real-world scenarios, explain why they happen, and share practical ways to avoid or fix them.

Understanding the Error Basics

At its core, this error occurs during JSON parsing in JavaScript. JSON (JavaScript Object Notation) is strict about its format: objects need matching curly braces {}, arrays need square brackets [], strings must be double-quoted, and so on. When JSON.parse() encounters the end of the input string without resolving the structure—like if a closing brace is missing—it throws this SyntaxError. It’s not just a frontend issue; it can crop up in Node.js environments too, especially with Express or when reading from files/storage.

One key thing to remember: JSON.parse() expects a string input. If you feed it something else, or if the string is incomplete, you’re in trouble. Now, let’s dive into the common culprits.

Common Source 1: Malformed or Incomplete JSON Structure

This is probably the top offender. Your JSON might have syntax errors like missing commas, unmatched brackets, or invalid keys (e.g., using hyphens in property names without quotes). The parser starts reading but runs out of input before it can make sense of it all.

For example, here’s a broken JSON string:

const badJson = '{"name": "Alice" "age": 30}'; // Missing comma after "Alice"

Trying to parse it:

try {
  const data = JSON.parse(badJson);
} catch (error) {
  console.error(error); // SyntaxError: Unexpected end of JSON input
}

To avoid this, always validate your JSON before parsing. Use online tools like JSONLint or built-in linters in your IDE. When generating JSON on the server side (e.g., in Node.js), double-check your object construction.

Fix it by adding the missing comma:

const goodJson = '{"name": "Alice", "age": 30}';
const data = JSON.parse(goodJson);
console.log(data); // { name: 'Alice', age: 30 }

Pro tip: If you’re building JSON dynamically, use JSON.stringify() on a valid object to ensure it’s formatted correctly from the start.

Common Source 2: Empty or Undefined Input

Sometimes, the input string is just empty—maybe an API returned nothing, or you’re pulling from localStorage that’s been cleared. The parser expects JSON but gets zilch, leading to the error.

Imagine this scenario with localStorage:

const userData = localStorage.getItem('user') || ''; // Could be empty
JSON.parse(userData); // Boom if empty

To dodge this, check for content first or provide a fallback:

const getUser = () => {
  const stored = localStorage.getItem('user');
  if (!stored) return null;
  try {
    return JSON.parse(stored);
  } catch (error) {
    console.error('Invalid user data:', error);
    return null;
  }
};

Or use a default empty object:

const data = JSON.parse(responseText || '{}');

This way, you’re handling the edge case gracefully without crashing.

Common Source 3: Parsing Non-JSON Data or Already Parsed Objects

You might accidentally try to parse data that’s not a JSON string—like an HTML error page from a server, or data that’s already been parsed into a JS object. For instance, if you’re using fetch() and the response is HTML (maybe a 404 page), parsing it as JSON will fail.

Bad example with fetch:

fetch('/api/data')
  .then(res => res.json())
  .then(data => console.log(data))
  .catch(err => console.error(err)); // Could error if not JSON

If the server sends HTML, you’ll get our infamous error.

To prevent it, always check the response status and content type first:

async function fetchData() {
  try {
    const res = await fetch('/api/data');
    if (!res.ok) {
      throw new Error(`HTTP error! Status: ${res.status}`);
    }
    const contentType = res.headers.get('Content-Type');
    if (!contentType || !contentType.includes('application/json')) {
      throw new Error('Response is not JSON');
    }
    return await res.json();
  } catch (error) {
    console.error('Fetch error:', error);
    return null;
  }
}

Also, if using libraries like Axios, they often handle this automatically, but verify in your setup.

Common Source 4: Truncated Data from Network Issues or Large Payloads

Network glitches can cut off data mid-transfer, leaving you with partial JSON. Or, the data might exceed parser limits, like in Express.js where the default JSON body limit is 100KB.

For large data in Node.js/Express:

const express = require('express');
const app = express();
app.use(express.json({ limit: '100kb' })); // Default might be too small

If data exceeds, it truncates, causing the error.

Bump the limit to avoid:

app.use(express.json({ limit: '50mb' })); // Adjust as needed

For network issues, implement retries or use libraries like fetch-retry. Always log responses in dev tools to spot truncations.

Common Source 5: Async Mishandling or Extra Whitespace

Forgetting to await a promise can lead to parsing undefined. Or, extra newlines/whitespace at the end of a JSON file can trip things up.

Async example without await:

const res = fetch('/api/data');
const json = res.json(); // res is a Promise, not resolved

Fix with async/await:

async function getData() {
  const res = await fetch('/api/data');
  const text = await res.text();
  if (text.trim()) { // Trim whitespace
    return JSON.parse(text);
  }
  return {};
}

Using .trim() handles extra spaces/newlines.

Additional Tips to Stay Error-Free

Building on what we’ve covered, wrap your parses in try-catch blocks universally—it’s a simple safety net. During development, mock data with tools like json-server or placeholders, as I mentioned earlier for network issues.

Test APIs with Postman or curl to ensure they return valid JSON. On the server, always set Content-Type: application/json and use methods like res.json() in Express.

If you’re dealing with user-generated JSON, sanitize inputs rigorously. And remember, for the latest JavaScript (ECMAScript 2023+), lean on modern features like optional chaining (data?.property) to handle parsed objects safely.

If you run into a variant not covered here, inspect the input string closely—console.log it before parsing. And if you’re really stuck – drop a comment below and I’ll take a look.

Was this helpful?

Thanks for your feedback!

Leave a comment

Your email address will not be published. Required fields are marked *