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

SituationExact message we measured
Extra comma in JS object literalSyntaxError: Unexpected token ','
Mismatched bracket in JSSyntaxError: Unexpected token ')'
JSON.parse('')SyntaxError: Unexpected end of JSON input
JSON.parse of an HTML error pageSyntaxError: Unexpected token '<', "...". is not valid JSON
JSON.parse with trailing comma in an objectSyntaxError: Expected double-quoted property name in JSON at position N
JSON.parse with trailing comma in an arraySyntaxError: Unexpected token ']', "..." is not valid JSON
import forced into a CommonJS fileSyntaxError: Cannot use import statement outside a module
export forced into a CommonJS fileSyntaxError: Unexpected token 'export'

5. Fixes

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.