Devpuff
Pricing
Log InStart Free
  1. Home
  2. Blog
  3. Unexpected token in JavaScript: the two different errors
Tools

Unexpected token in JavaScript: the two different errors

Unexpected token is two different errors. One is a typo in your file, the other means you got HTML where you expected JSON.

By Max Arthur
Co-Founder & Content Marketer·October 4, 2026·7 Min read
White text reading 'Unexpected token in JavaScript' on a dark purple dotted background

Unexpected token is two unrelated errors wearing the same name. One is a typo in a file you wrote, caught before any of your code runs. The other happens at runtime when JSON.parse is handed something that is not JSON, which is almost always an HTML page.

They have different causes and different fixes, so the first move is to work out which one you have.

Look at the end of the message. If it says is not valid JSON, it is the runtime kind and your code is fine. If instead it points at a file and line with a caret underneath, it is the parse kind and there is a character to fix.

Every message on this page came out of Node v24.14.1.

The parse error: a typo in your file

This version stops the file loading. Nothing in it runs, which is why adding console.log at the top changes nothing.

Node prints the offending line with a caret under the exact character:

const o = {a: 1,, b: 2}
                ^

SyntaxError: Unexpected token ','

The caret is the useful part. The character it points at is not necessarily wrong, but it is where the parser gave up, and the real mistake is at or just before it.

Common versions, all confirmed:

Code Message
if (x > 1 { Unexpected token '{'
function f() { return 1 }} Unexpected token '}'
const class = 5 Unexpected token 'class'
function f(a, b,,) {} Unexpected token ','

The third one catches people out. class is a reserved word, and so are const, new, return, default and about thirty others. You cannot use them as variable names, and the message does not explain that, it just reports the word as unexpected.

When a bracket is missing you get a different message

This is worth knowing because it changes where you look:

function greet() {
  console.log("hi")

console.log("after")
SyntaxError: Unexpected end of input

Not "unexpected token". When you leave a bracket open, the parser reads to the end of the file still waiting for it, and reports running out of input. So:

  • Unexpected token '}' means one too many closing brackets.
  • Unexpected end of input means one too few.

That single distinction tells you which direction to search, which matters in a long file. Your editor's bracket matching will find the pair faster than reading will.

The near-miss messages

Three more parse failures produce wording close enough to confuse, and each points somewhere specific:

Code Message What it means
export const a = 1 in a .cjs file Unexpected token 'export' Module format mismatch
const s = "unterminated Invalid or unexpected token A quote is never closed
{ "a": 1 "b": 2 } Unexpected string Missing comma between properties

The first is the one to recognise on sight. Unexpected token 'export' or Unexpected token 'import' almost never means you mistyped the keyword. It means the file is being read as CommonJS while containing ES module syntax. Either rename the file to .mjs, add "type": "module" to package.json, or convert the syntax to require. The mirror image, require inside an ES module, does not produce a syntax error at all: it reports require is not defined in ES module scope.

The other two are useful because the wording tells you the shape of the mistake before you have found it. Invalid or unexpected token points at a string that never ends, and the highlighting in your editor will show it as a long run of string colour. Unexpected string means the parser met a value where it wanted a separator, which is a missing comma nearly every time.

The < that means you got HTML

This is the most common version of the error in real projects, and it has nothing to do with your syntax:

JSON.parse("<!DOCTYPE html>\n<html><body>Cannot GET /api/users</body></html>");
// SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

Read the message closely, because it is more helpful than it looks. It quotes the first characters of what it was given. "<!DOCTYPE " is the beginning of an HTML page. Your request returned a web page, and the code tried to parse it as data.

Nearly always, this means the request went somewhere that does not serve JSON:

  • The URL is wrong, so the server returned its 404 page. An Express 404 gives Unexpected token 'C', "Cannot GET"..., which quotes the response body back at you and names the problem outright.
  • The request hit the frontend dev server instead of the API, so it returned index.html. This is the classic missing-proxy case.
  • You are logged out, and the server sent a login page instead of data.
  • The server threw, and returned an HTML error page with a 500 status.

The fix is never in the parsing code. Open the Network tab, find the request, and look at the response body and status. What the message quotes is what the server sent.

Two versions of the same < appear in source files rather than responses:

SyntaxError: Unexpected token '<'

A .js file whose contents are actually HTML produces this, usually because a <script src> points at the wrong path and the server returned a page. So does JSX in a file that is not being compiled, since const el = <h1>hi</h1> is not valid JavaScript on its own. All three cases share one lesson: a < where JavaScript expected an expression means something handed you markup.

The JSON errors that are not <

The rest of the JSON.parse failures are usually malformed data rather than the wrong data:

Input Message
"" (empty response) Unexpected end of JSON input
{"a": 1,} Expected double-quoted property name in JSON at position 8
{'a': 1} Expected property name or '}' in JSON at position 1
undefined "undefined" is not valid JSON

The empty-response case is worth calling out. A 204 No Content reply has an empty body by design, so calling .json() on it always throws. Check the status before parsing rather than after.

The trailing comma is the one that surprises people who write a lot of JavaScript. {a: 1,} is legal in a .js file and illegal in JSON. JSON also requires double quotes on keys, which is why the single-quoted version fails at position 1.

Note that these messages give a character position rather than a line and column of your source. at position 8 counts into the string being parsed, so logging the raw text and counting to that character finds it.

Preventing both kinds

For the parse error, the tooling does it for you. An editor with syntax highlighting shows an unterminated string as a wall of the wrong colour, and a linter reports the problem as you type rather than at run time. A formatter such as Prettier will refuse to format a file it cannot parse, which is a useful early signal in itself.

For the JSON error, stop trusting that a response is JSON:

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

Checking res.ok first turns a confusing Unexpected token '<' into a clear Request failed: 404, because fetch does not treat a 404 as a failure. It resolves normally and hands you the error page, which is exactly how the HTML ends up in the parser. Only a network-level failure rejects the promise, so a .catch() on its own will not save you from this.

The habit worth building is reading the whole message rather than the first three words. Unexpected token '<', "<!DOCTYPE "... already tells you the server sent a web page. Most of the time the answer is in the part after the comma.

Want to see these fail in front of you? Start with the JavaScript track and run the broken versions in an editor.

Keep reading

More from the blog

White text reading 'Objects are not valid as a React child' on a dark purple dotted background
October 7, 2026·7 min readTools

Objects are not valid as a React child: what to render

Objects are not valid as a React child, and React tells you which one by listing its keys. In React 19 the promise version throws a different error.

Read more
White text reading 'How to fix npm install errors' on a dark purple dotted background
October 6, 2026·8 min readTools

npm install errors: read the code, not the stack

Every npm ERR code names one cause. ERESOLVE, E404, EJSONPARSE and the rest, with what each one means, measured against real installs.

Read more
{ }
✦

Ready to write some code?

Put this into practice - start your first free lesson. No setup, no credit card.

Start learning free
Devpuff

Learn to code by doing. One tiny, playful lesson at a time.

Learn
ProgramsCoursesPricing
Company
AboutBlogResourcesAffiliates
Support
Help CenterContactStatus
Programs
Frontend DevelopmentFull-Stack DevelopmentReact DeveloperPython Developer
Courses
Advanced ReactAsync JavaScriptAsync PythonCSS BasicsCSS LayoutDSA Basics
Learn to Code
Learn JavaScriptLearn PythonLearn SQLLearn HTMLLearn CSSLearn ReactBrowse All Topics
Platform Comparisons
Devpuff vs CodecademyDevpuff vs Mimo
© 2026 Devpuff. All rights reserved.Privacy PolicyTerms and ConditionsCookies PolicyRefund Policy
Devpuff vs Sololearn
Devpuff vs freeCodeCamp
Read All Comparisons