jsondecode.com logo

HomeChevronBlogChevronJSON Error Handling Best Practices

Blog post

JSON Error Handling Best Practices

Handle JSON errors gracefully — parse failures, API response errors, schema mismatches, and network issues with code examples.

author

Shashank Jain

Author

14/06/20262 minutes 20 seconds read
JSON Error Handling Best PracticesJSON Error Handling Best Practices

Article

Why JSON Error Handling Matters

JSON errors are among the most common runtime failures in web applications. An API returns HTML instead of JSON, a user pastes malformed input, or a network error truncates a response — any of these crashes an app that does not handle errors properly.

Safe JSON.parse in JavaScript

// Never do this — crashes on invalid input
const data = JSON.parse(userInput);

// Always do this
function safeParseJSON(str, fallback = null) {
  try {
    return JSON.parse(str);
  } catch (e) {
    console.error('JSON parse failed:', e.message);
    return fallback;
  }
}

const data = safeParseJSON(userInput, {});

Handling API JSON Errors

async function fetchJSON(url) {
  const res = await fetch(url);

  // Check HTTP status before parsing
  if (!res.ok) {
    throw new Error(`HTTP ${res.status}: ${res.statusText}`);
  }

  // Check Content-Type — API might return HTML on error
  const contentType = res.headers.get('content-type');
  if (!contentType?.includes('application/json')) {
    const text = await res.text();
    throw new Error(`Expected JSON, got ${contentType}: ${text.slice(0, 200)}`);
  }

  return res.json();
}

// Usage
try {
  const data = await fetchJSON('/api/users');
} catch (e) {
  console.error('Failed to fetch:', e.message);
}

TypeScript: Type-Safe JSON Parsing

import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email()
});

type User = z.infer<typeof UserSchema>;

async function getUser(id: number): Promise<User> {
  const res = await fetch(`/api/users/${id}`);
  const raw = await res.json();
  return UserSchema.parse(raw); // throws ZodError if invalid
}

Python Error Handling

import json
from typing import Any, Optional

def safe_parse(text: str, fallback: Any = None) -> Any:
    try:
        return json.loads(text)
    except json.JSONDecodeError as e:
        print(f'JSON error at line {e.lineno}, col {e.colno}: {e.msg}')
        return fallback

# Requests library
import requests

def fetch_json(url: str) -> Optional[dict]:
    try:
        res = requests.get(url, timeout=10)
        res.raise_for_status()  # Raises for 4xx/5xx
        return res.json()
    except requests.exceptions.JSONDecodeError:
        print('Response is not valid JSON')
    except requests.exceptions.HTTPError as e:
        print(f'HTTP error: {e}')
    except requests.exceptions.RequestException as e:
        print(f'Network error: {e}')
    return None

Common Error Patterns

ErrorCauseFix
SyntaxError: Unexpected token <API returned HTML error pageCheck Content-Type header before parsing
SyntaxError: Unexpected endTruncated responseCheck network, increase timeout
TypeError: Cannot read property of nullParsed null, not objectDefault to {} or [] after parsing
JSON decode error at position NEncoding mismatch or BOMNormalize encoding, strip BOM

FAQ

Should I always wrap JSON.parse in try/catch?

Yes, whenever parsing untrusted or external data — user input, API responses, file contents. Skip try/catch only when the JSON comes from your own serializer in the same request/response cycle.

How do I get the line number of a JSON syntax error?

In Python, json.JSONDecodeError has lineno and colno attributes. In JavaScript, the Error message includes position info but not structured. Use a linting library like jsonlint for detailed error positions.

What is the best way to validate JSON structure after parsing?

Use Zod (TypeScript/JavaScript) or Pydantic (Python) for runtime schema validation. Both provide clear error messages and TypeScript/Python type inference from the schema.

How do I handle partial JSON responses from streaming APIs?

Buffer incoming chunks and attempt JSON.parse only when you receive the closing delimiter. For structured streaming, use NDJSON (newline-delimited JSON) — each line is a complete valid JSON object.

Why does fetch().json() fail even when the response looks right?

Common causes: Content-Type header is missing or wrong, response body is empty, or there is a BOM character at the start. Log res.text() first to inspect the raw response before parsing.

Keep reading

Recent blogs

View all

If jsondecode.com saved you time, share it with your team

Free forever. No ads. No sign-up. Help other developers find it.