SyntaxError: Unexpected token '<' is one of the most common JSON parsing errors on the web. It happens when JavaScript tries to parse HTML as JSON - that < character is usually the start of an HTML tag like <!DOCTYPE html> or <html>.

This error appears in your console when JSON.parse() encounters HTML instead of valid JSON data. It’s particularly frustrating because it often masks the real problem: your server returned an error page instead of the API response you expected.

The Problem

Your JavaScript code makes an API request expecting JSON, but the server returns HTML instead:

fetch('/api/users')
  .then(response => response.json()) // 💥 SyntaxError: Unexpected token '<'
  .then(data => console.log(data))

The response.json() method tries to parse the response body as JSON, but when it encounters that first < character from the HTML, it throws a SyntaxError.

Understanding the Root Cause

When servers encounter errors or certain conditions, they often return HTML error pages instead of JSON responses. This mismatch between expected and actual content types causes the parsing failure.

503 Service Unavailable / 502 Bad Gateway

Load balancers and reverse proxies return HTML error pages when backend services are down:

<!DOCTYPE html>
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<h1>Service Temporarily Unavailable</h1>
<p>The server is temporarily unable to service your request.</p>
</body>
</html>

Authentication Failures

When authentication fails, servers often redirect to an HTML login page instead of returning a proper JSON error response.

CORS Errors

Some browsers return an opaque HTML document when CORS requests fail, though this is less common in modern browsers.

Network Interception

Corporate proxies, hotel WiFi, and public networks often inject HTML login or payment pages when intercepting requests.

How to Fix Unexpected Token ‘<’

The key is to validate the response before attempting to parse it as JSON.

Troubleshooting Checklist

  • Check the Network tab in DevTools - what’s the actual response?
  • Verify the response Content-Type header
  • Confirm the HTTP status code
  • Test the API endpoint directly (via Postman or curl)
  • Check server logs for errors during the request

Step 1: Check Content-Type Before Parsing

Always verify you received JSON before parsing:

fetch('/api/users')
  .then(response => {
    const contentType = response.headers.get('content-type');
    if (!contentType || !contentType.includes('application/json')) {
      throw new TypeError("Response wasn't JSON");
    }
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

Step 2: Handle HTTP Errors Properly

Check the response status before parsing:

fetch('/api/users')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

Step 3: Implement Safe Parsing

For maximum safety, read as text first:

async function safeJsonFetch(url) {
  try {
    const response = await fetch(url);
    const text = await response.text();

    // Check if response is HTML
    if (text.startsWith('<!DOCTYPE') || text.startsWith('<html')) {
      console.error('Received HTML instead of JSON:', text);
      throw new Error('Server returned HTML instead of JSON');
    }

    // Try to parse as JSON
    try {
      return JSON.parse(text);
    } catch (e) {
      console.error('Invalid JSON:', text);
      throw new Error('Server returned invalid JSON');
    }
  } catch (error) {
    console.error('Fetch error:', error);
    throw error;
  }
}

Step 4: Monitor Network Errors in Production

This is not the sort of error you will find during testing. It only pops up in production when one of your APIs goes down. To know when that happens, you’ll need to set up frontend monitoring.

When to Ignore This Error

You might choose to ignore this error when:

  • Third-party APIs: If an external service you don’t control occasionally returns HTML errors
  • Non-critical features: When the failed request doesn’t impact core functionality
  • Known gateway issues: During scheduled maintenance or known infrastructure problems
  • Legacy endpoints: Old APIs that occasionally misbehave but are being phased out

However, you should still log these occurrences to monitor frequency and patterns.

Summary

Unexpected token '<' means your JavaScript expected JSON but got HTML. This usually happens when servers return error pages instead of proper API responses. To fix it:

  1. Always validate response content-type before parsing
  2. Check HTTP status codes
  3. Implement proper error handling
  4. Add retry logic for transient failures
  5. Log errors for monitoring

The root cause is almost always on the server side - either an actual server error or a misconfigured endpoint. But your client-side code needs to handle these cases gracefully to provide a good user experience.

TrackJS is the easy way to monitor your JavaScript applications and fix production errors. TrackJS is provides detailed error monitoring and alerting to developers around the world at companies like 3M, Tidal, IKEA, Venmo, Allbirds, and Frontend Masters. TrackJS catches millions of errors from users everyday. Let's start catching yours.

Protect your JavaScript