Why does JavaScript say “Invalid or unexpected token”?
Because the parser hit a code point it cannot use to start or continue any valid token at that exact position — not a keyword, not part of an identifier, not an operator, and not whitespace it recognizes. The code usually looks completely normal, because the character causing the failure has no visible glyph or looks identical to the one it replaced.
In a browser console the message is prefixed Uncaught: Uncaught SyntaxError: Invalid or unexpected token. In Node.js, the same underlying V8 parser prints the offending source line with a caret under the exact character, then SyntaxError: Invalid or unexpected token on its own line. Both come from the identical engine — Chrome and Node.js both run V8 — so the message text and the character's behavior are the same in either environment.
What the error looks like, verified character by character
The rows below come from actually running each snippet in Node.js 22.22.2 and in headless Chromium — not a forum post. Each line assigns a value next to the character in question:
| Character | Test | Result in V8 (Node.js & Chromium) |
|---|---|---|
| Non-breaking space (U+00A0) | const foo = 5; | Runs. No error — treated as whitespace. |
| Zero-width space (U+200B), inside a const declaration | const foo = 5; | SyntaxError: Missing initializer in const declaration |
| Zero-width space (U+200B), inside a plain assignment | x = 2; | SyntaxError: Invalid or unexpected token |
| Curly quotes (U+201C / U+201D) in place of straight quotes | const s = “hello”; | SyntaxError: Invalid or unexpected token |
The two zero-width-space rows use the exact same character. Only the surrounding grammar changes which message V8 prints — a detail worth knowing before you trust the message text over the caret position.
Why doesn't a non-breaking space break JavaScript?
Because the ECMAScript specification puts it on the whitespace list by name. The White Space clause of ECMA-262 defines a <USP> category for "any code point in general category Space_Separator," with a note stating plainly: "U+0020 (SPACE) and U+00A0 (NO-BREAK SPACE) code points are part of <USP>." A non-breaking space is therefore valid ECMAScript whitespace everywhere a regular space is, which is exactly what the test above confirms.
That makes JavaScript the opposite of Python here. The guide to Python's invalid-character SyntaxError found that CPython rejects a non-breaking space outside a string with invalid non-printable character U+00A0. The same character, copied from the same PDF or the same chat interface, crashes one language and is silently accepted by the other — accepted, but still worth removing, since it still looks identical to a normal space to every human reading the file, and can trip up whitespace-sensitive diffs, linters, and string comparisons even without a parser error.
How is Firefox's error different from Chrome's or Node's?
Firefox's JavaScript engine, SpiderMonkey, takes a more direct approach: per MDN's "SyntaxError: illegal character" reference page, it names the exact code point in the message itself — SyntaxError: illegal character U+200B for a zero-width space, for example. V8 never does this: as the table above shows, Chrome and Node.js report either a generic Invalid or unexpected token or a context-dependent message that doesn't mention a character at all. If you can reproduce a paste-related syntax error in Firefox's console before debugging it in Chrome or Node, its message alone may already tell you which character to remove.
Can an invisible character break JavaScript without any error?
Yes — inside a string literal, template literal, or comment, the same characters that crash a bare expression are accepted without complaint, because that is exactly where the specification says whitespace and other code points are "considered significant code points forming part of a literal value." Tested directly:
> "hello\u200bworld".length 11 > "hello\u200bworld" === "helloworld" false
No error, no warning — just a string one character longer than it looks, failing an equality check that should obviously pass. This is the case to actually worry about: a SyntaxError tells you exactly where to look, but a zero-width space sitting inside a string constant, an object key, or an API parameter changes behavior with zero diagnostic. It's the same class of problem covered in more depth by the guide to sanitizing copied text for code, JSON, and CSV.
How do I fix it, and stop it from recurring?
- Trust the caret, not the wording. As the const-declaration case shows, the message text can describe a completely different problem than "there's a hidden character here." The position under the caret is what actually points at the culprit.
- Check for curly quotes first if a string is involved. Code copied out of a chat interface, a slide deck, or a word processor frequently substitutes curly quotes for straight ones, which is the single most common cause of this error in copied code that otherwise looks correct.
- Run the file through a character-level cleaner if the error recurs on different lines — that pattern usually means the source you copied from keeps injecting the character rather than it being a one-off typo. Paste the snippet above and check the report: it lists every hidden character by name and code point before you copy the cleaned version back into your editor.
- Don't assume "no error" means "no problem." As shown above, the same characters are accepted silently inside strings. A clean pass through the invisible character remover catches those too, entirely in your browser, without altering indentation or any character you can actually see.