JavaScript 'Unexpected token' (tested)
Unexpected token is a generic SyntaxError name used by both JavaScript's own parser and JSON.parse(), which has a stricter, different grammar. The same words show up for a real typo, a bad API response you tried to parse as JSON, and ES module syntax in the wrong kind of file. We reproduced all three in Node.js v24.21.0.
1. A real JavaScript syntax mistake
An extra comma in an object literal:
const obj = {
a: 1,,
b: 2
};
// SyntaxError: Unexpected token ','
A mismatched bracket:
function f() {
return [1, 2, 3);
}
// SyntaxError: Unexpected token ')'
These crash immediately, before any of the file's code runs — the parser rejects the whole file. The message names the exact character the parser didn't expect, and the pointer (^) in Node's output lands on it directly.
2. JSON.parse() on invalid or unexpected text
JSON.parse() uses the strict JSON grammar, not JavaScript's, so the wording differs by what's wrong. We measured each case separately:
JSON.parse('');
// SyntaxError: Unexpected end of JSON input
JSON.parse('<html><body>404</body></html>');
// SyntaxError: Unexpected token '<', "<html><bod"... is not valid JSON
JSON.parse('{"a":1,}');
// SyntaxError: Expected double-quoted property name in JSON at position 7 (line 1 column 8)
JSON.parse('[1,2,]');
// SyntaxError: Unexpected token ']', "[1,2,]" is not valid JSON
An empty string is the classic symptom of parsing a response body that was actually empty (a 204, or a failed request with no body) — see Unexpected end of JSON input for that one on its own. The '<' case is what you get parsing an HTML error page (a 404 or 500 page) as if it were JSON — the giveaway is the literal < character and an HTML snippet quoted in the message.
Trailing commas are the sharpest trap: {"a":1,} and [1,2,] are both invalid JSON (the JSON spec forbids trailing commas) but valid JavaScript — we confirmed the same text runs fine as a JS object literal via Function:
const f = Function('return {"a":1,}');
f();
// { a: 1 } -- no error, because this is JS, not JSON.parse()
So code copied from a JS object literal (with a trailing comma left in) will fail specifically when run through JSON.parse(), even though it looks fine as JavaScript.
3. import/export syntax in a CommonJS file
Node 24 auto-detects ES module syntax in a plain .js file with no package.json nearby and just runs it as a module — we confirmed a bare export const x = 1; in an extension-less-config .js file ran with no error at all. The classic error only appears once a file is forced into CommonJS, either via a .cjs extension or an explicit "type": "commonjs" in the nearest package.json. We measured both forms:
// forcedCjs.cjs
import { foo } from './bar.js';
// SyntaxError: Cannot use import statement outside a module
// forcedCjs2.cjs
export const x = 1;
// SyntaxError: Unexpected token 'export'
import gets its own named message ("Cannot use import statement outside a module"); export falls through to the generic "Unexpected token 'export'" because, to a CommonJS parser, export isn't a reserved keyword it treats specially — it's just a token in a place that doesn't parse. Both are real Node.js v24.21.0 output; we also saw a companion warning line pointing at "type": "module" or the .mjs extension as the fix, which is accurate.
4. Side-by-side comparison
| Situation | Exact message we measured |
|---|---|
| Extra comma in JS object literal | SyntaxError: Unexpected token ',' |
| Mismatched bracket in JS | SyntaxError: Unexpected token ')' |
| JSON.parse('') | SyntaxError: Unexpected end of JSON input |
| JSON.parse of an HTML error page | SyntaxError: Unexpected token '<', "...". is not valid JSON |
| JSON.parse with trailing comma in an object | SyntaxError: Expected double-quoted property name in JSON at position N |
| JSON.parse with trailing comma in an array | SyntaxError: Unexpected token ']', "..." is not valid JSON |
import forced into a CommonJS file | SyntaxError: Cannot use import statement outside a module |
export forced into a CommonJS file | SyntaxError: Unexpected token 'export' |
5. Fixes
- Real syntax error: Node's own pointer (
^under the bad character) is accurate — look exactly there, one character at a time, for a stray or missing comma/bracket/quote. - JSON.parse() failing: log the raw text before parsing it (
console.log(JSON.stringify(text))). An empty string or anything starting with<means you parsed the wrong response (check the HTTP status first); a trailing comma means the text was hand-written or template-built as JS, not real JSON — run it through a JSON validator/linter orJSON.stringify(JSON.parse(...))round-trip during development to catch it early. - import/export in the wrong file type: either rename the file to
.mjs, or add"type": "module"to the nearestpackage.json. If you need to stay CommonJS, userequire()/module.exportsinstead — see require is not defined in ES module scope for the opposite mismatch.
If the token in question is specifically end-of-input on an otherwise-empty response body, see Unexpected end of JSON input for that case on its own. For the equivalent error in a different language, see bash: syntax error near unexpected token.
Frequently asked questions
Why does 'Unexpected token' show up in so many unrelated bugs?
It's a generic SyntaxError name both JavaScript's own parser and JSON.parse() use whenever they hit a character or keyword that doesn't fit the grammar at that point. We measured it for a stray comma, a mismatched bracket, bad JSON, and import/export in the wrong file type -- the fix is different in each case, so the exact wording and where it points matters more than the headline.
Why does my JSON.parse() error mention an HTML tag?
You likely parsed an HTML error page (a 404 or 500 response) as if it were JSON. We measured this directly: parsing '<html>...' with JSON.parse() gives SyntaxError: Unexpected token '<', with the HTML snippet quoted in the message. Check the HTTP status code before parsing the body.
Is a trailing comma valid JSON?
No, even though it's valid JavaScript. We confirmed {"a":1,} and [1,2,] are both invalid JSON (JSON.parse throws on each) but run fine as JS object/array literals. Code copied from JS source with a trailing comma left in will fail specifically through JSON.parse().
Why did my import/export syntax work in one file but not another?
Node decides module type per file. We measured that a plain .js file with no package.json nearby gets auto-detected as a module and just works, but the same code forced into CommonJS (.cjs extension, or "type": "commonjs" in package.json) throws: 'Cannot use import statement outside a module' for import, or the generic 'Unexpected token 'export'' for export.
How do I quickly tell a real JS syntax error from a JSON.parse() error?
Check which function is on the stack trace. A real syntax error crashes while Node is loading your file, before any of your code runs, and the trace has no JSON.parse frame. A JSON.parse() error happens while your code is already running, at the specific line calling JSON.parse(), and the message includes the literal text it failed on.
Written with AI assistance. Measured on 2026-10-03 with Node.js v24.21.0 on Linux. Every snippet was run as a real file or expression and the output is copied verbatim. PlainHeaders is our extension; Node.js is free.