πŸ”§ Error Fixes

SyntaxError: Unexpected Token < in JSON β€” How to Fix It


SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON
SyntaxError: Unexpected token < in JSON at position 0

You called response.json() but the server returned HTML (probably an error page) instead of JSON. The < is the start of <!DOCTYPE html>.

Fix 1: Wrong URL

The most common cause. Your fetch URL is wrong, and the server returned a 404 HTML page.

// ❌ Typo in URL β€” server returns 404 HTML page
const res = await fetch('/api/uers');  // "uers" not "users"

// βœ… Correct URL
const res = await fetch('/api/users');

Always check the response before parsing:

const res = await fetch('/api/users');
if (!res.ok) {
  throw new Error(`HTTP ${res.status}: ${res.statusText}`);
}
const data = await res.json();

Fix 2: API Server Not Running

Your frontend is running but the API server isn’t. The dev server returns its own HTML 404 page.

# Check if the API is actually running
curl http://localhost:3000/api/users

If you get HTML back, your API isn’t running or isn’t on that port.

Fix 3: Proxy Not Configured (Vite / CRA)

In development, your frontend runs on port 5173 and your API on port 3000. Without a proxy, /api/users hits the frontend server.

// vite.config.ts
export default {
  server: {
    proxy: {
      '/api': 'http://localhost:3000',
    },
  },
};

Fix 4: CORS Redirect

The server redirected your API request to a login page (HTML).

// Check what you actually got back
const res = await fetch('/api/users');
const text = await res.text();
console.log(text);  // See the actual response

If it’s a login page, you need to handle authentication first.

Fix 5: Production Build Serving HTML

In production, a catch-all route (for SPA routing) might serve index.html for API routes too.

# ❌ Nginx catches /api routes and serves index.html
location / {
    try_files $uri $uri/ /index.html;
}

# βœ… API routes go to the backend first
location /api/ {
    proxy_pass http://localhost:3000;
}
location / {
    try_files $uri $uri/ /index.html;
}

Debugging Pattern

Always check what you got before parsing:

const res = await fetch(url);
const contentType = res.headers.get('content-type');

if (!contentType || !contentType.includes('application/json')) {
  const text = await res.text();
  console.error('Expected JSON, got:', text.substring(0, 200));
  throw new Error('Response is not JSON');
}

const data = await res.json();