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.
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
- Is it
JSON.parse/res.json()? Log the rawawait res.text()and the HTTP status first — you will usually see HTML or an empty string. - Token is
'<'? Check the network response (status + content-type), not your code. - Token is
import/export? Checkpackage.json"type"and the file extension (.mjs/.cjs). - JSX involved? Confirm a compiler (Babel / esbuild / SWC) runs on
.jsx/.tsx— nevernode file.jsx. - Hand-written JSON? Scan for trailing commas, comments and single quotes.
Frequently asked questions
Why does JSON.parse throw "Unexpected token <"?
< 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"?
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?
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.