Blog

Blog · Error Fixes

How to Fix "Unexpected Token" in JavaScript & JSON

SyntaxError: Unexpected token usually means the input is not the syntax the parser expected — HTML fed to JSON.parse, uncompiled JSX, or an ESM/CommonJS mismatch. Here is the fix for each.

XCODX Team · 3 min read

SyntaxError: Unexpected token means a parser — V8, JSON.parse, or a bundler — hit a character that is not valid at that position. It almost always means the input is not the syntax the parser expected: HTML sent to JSON.parse, JSX that never got compiled, or ESM/CommonJS keywords under the wrong module system.

Unexpected token "<" — HTML fed to JSON.parse

The most common case. A fetch returned an HTML error page (404, 500, a login redirect) but the code parsed it as JSON — the < is the start of <!DOCTYPE. Modern Node/Chrome word it Unexpected token '<', "<!DOCTYPE "... is not valid JSON.

// ✅ check status and content-type before parsing
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const ct = res.headers.get("content-type") || "";
if (!ct.includes("application/json")) {
  throw new Error(`Not JSON: ${await res.text()}`);
}
const data = await res.json();

Unexpected end of JSON input — empty response

The body is empty (a 204 No Content, or a failed request). JSON.parse("") throws.

// ✅ read text first, then branch on empty
const t = await res.text();
const data = t ? JSON.parse(t) : null;

Unexpected token "<" — JSX without a compiler

Raw JSX ran through a plain JavaScript parser (running node App.jsx, or a loader not configured for JSX).

// ❌ do not run JSX directly through node
// node App.jsx   ->   Unexpected token '<'

// ✅ compile it: use a bundler (Vite / webpack + Babel) or the
//    framework toolchain, ensure @babel/preset-react (or esbuild/SWC
//    JSX) is enabled, and use a .jsx / .tsx extension.

import/export in the wrong module system

Cannot use import statement outside a module means ESM syntax in a CommonJS file; Unexpected token 'export' (or require is not defined) means the reverse.

// package.json decides: add "type": "module" for ESM, or use
// .mjs (always ESM) / .cjs (always CommonJS).

// ESM file:
import express from "express";
export default app;

// CommonJS file:
const express = require("express");
module.exports = app;

Trailing commas or comments in JSON

JSON is stricter than a JavaScript object literal: no trailing commas, no comments, no single quotes, and keys must be double-quoted.

// ❌ trailing comma -> Unexpected token }
JSON.parse('{"a":1,}');
// ✅
JSON.parse('{"a":1}');

Quick diagnostic checklist

  1. Is it JSON.parse / res.json()? Log the raw await res.text() and the HTTP status first — you will usually see HTML or an empty string.
  2. Token is '<'? Check the network response (status + content-type), not your code.
  3. Token is import / export? Check package.json "type" and the file extension (.mjs / .cjs).
  4. JSX involved? Confirm a compiler (Babel / esbuild / SWC) runs on .jsx / .tsx — never node file.jsx.
  5. Hand-written JSON? Scan for trailing commas, comments and single quotes.

Frequently asked questions

Why does JSON.parse throw "Unexpected token <"?
The response you parsed is HTML, not JSON — usually an error page or a redirect. The < is the start of <!DOCTYPE. Check the HTTP status and content-type, and log the raw response text before parsing.
Why do I get "Cannot use import statement outside a module"?
You used ESM import/export in a file Node treats as CommonJS. Add "type": "module" to package.json, rename the file to .mjs, or switch to require/module.exports.
Can JSON have comments or trailing commas?
No. Strict JSON forbids comments, trailing commas and single-quoted strings, and requires double-quoted keys. Config files like tsconfig use a relaxed JSONC that allows comments, but JSON.parse does not.

Paste your JSON or JS into XCODX Studio — it parses and runs in the browser and points to the exact character the parser rejected, so a stray token is obvious at a glance.