“JSON.parse: unexpected character”

That exact wording — JSON.parse: unexpected character at line L column C of the JSON data — is Firefox’s phrasing for a fault every browser reports differently. Chrome and Node call it “unexpected token,” Safari calls it “JSON Parse error,” and none of the three wordings look related at a glance even though they describe the same broken document. Paste it below and get a caret at the exact character, regardless of which browser you read the error in.

Indent

Paste the raw response, credentials and all. There is no upload endpoint, no storage and no share link on this page — the parser runs in your tab. A debugging payload full of tokens goes no further than your own browser. Why that is worth checking on any tool you use →

Searching a different exact string? This page covers Firefox’s generic JSON.parse: unexpected character wording. See also “Unexpected token < … position 0”, “Unexpected end of JSON input”, or the full token-by-token reference covering all sixteen causes with the Chrome/Node wording.

One fault, four unrelated-looking messages

There is no such thing as a universal JSON error message — every JavaScript engine writes its own parser, in its own words, and none of the four major ones agree on phrasing. RFC 8259 defines what is and is not valid JSON with total precision; it says nothing at all about what an implementation must print when input violates it. That silence is why the same broken document produces four differently worded errors depending on where it runs, and why a search for the exact string in front of you can miss pages written around a different engine’s phrasing of an identical underlying mistake:

EngineRuns inGeneric wording for this fault
SpiderMonkey Firefox JSON.parse: unexpected character at line L column C of the JSON data
V8, current Chrome, Node ≥ 20, Edge, Deno Unexpected token 'X', "…"... is not valid JSON
V8, older Chrome < 118, Node < 20 Unexpected token X in JSON at position N
JavaScriptCore Safari, Bun JSON Parse error: Unrecognized token 'X'

Firefox’s unexpected character phrasing is its catch-all: it appears whenever the very next character cannot begin any legal JSON token in the current context, which is the same condition Chrome describes as an unexpected token and Safari as an unrecognised token. This page treats all three as one fault and gives the fix once.

The exact trigger determines the exact wording

Not every invalid character produces the generic message in every engine — some triggers get a more specific message instead, because the parser was already in a grammar position (such as expecting a property name) where it can name what it wanted. The table below is measured directly against current Chromium, Firefox and WebKit, not guessed:

InputChrome/Node (V8)FirefoxSafari
'hello' Unexpected token ''' unexpected character Single quotes (') are not allowed in JSON
[1,2,] Unexpected token ']' unexpected character Unexpected comma at the end of array expression
{"a":1,} Expected double-quoted property name expected double-quoted property name Property name must be a string literal
{a:1} Expected property name or '}' expected property name or '}' Expected '}'
NaN / Infinity "NaN" is not valid JSON unexpected character Unexpected identifier "NaN"
// or /* comment Unexpected token '/' unexpected character Unrecognized token '/'
BOM (U+FEFF) first Unexpected token '�' unexpected character Unrecognized token '�'
“smart quotes” Expected property name or '}' expected property name or '}' Unrecognized token '“'

The pattern: Firefox collapses almost everything that is not a specific grammar position into one generic “unexpected character” message, while Chrome and Safari more often name the specific token. If you searched the Firefox wording and landed here, your actual trigger is very likely one of the causes below, worded differently in whatever engine produced the page you are debugging against.

The causes, most common first

1. Single quotes instead of double quotes

Invalid
{'name': 'Ada'}
Valid
{"name": "Ada"}

JSON strings are double-quoted, without exception. This most often arrives from a JavaScript object literal pasted rather than serialised, or from Python’s str(dict) / repr() output, which resembles JSON closely enough to be mistaken for it.

Fix: in Python, use json.dumps(d), never str(d). In JavaScript, build the value and call JSON.stringify rather than writing the text by hand.

2. A trailing comma

Invalid
{
  "port": 5432,
}
Valid
{
  "port": 5432
}

Allowed in JavaScript, Python, Rust and Go object/array literals; never allowed in JSON. This is the single most common hand-editing mistake, because every modern language and linter has trained developers that a trailing comma is harmless.

Fix: delete the comma. If the document is generated, stop building it by string concatenation and serialise a real value instead — a JSON library cannot produce a trailing comma.

3. Unquoted property names

Invalid
{name: "Ada", age: 36}
Valid
{"name": "Ada", "age": 36}

A perfectly valid JavaScript object literal, and invalid JSON — every key must be a double-quoted string, including keys that look like plain identifiers.

4. NaN, Infinity, undefined, None

Not JSONUsually comes fromWrite instead
NaNA float division gone wrong upstreamnull
InfinityOverflow or a deliberate sentinelnull
undefinedJavaScript string concatenationnull
None, True, FalsePython’s str(dict)null, true, false

JSON has no representation for not-a-number or infinity. Python’s json.dumps will emit NaN by default unless you pass allow_nan=False, producing a file that no strict JSON parser, including every browser’s, can read back.

5. A byte order mark

A UTF-8 BOM — the invisible character U+FEFF, written by Notepad, PowerShell redirection and Excel’s export by default — is legal inside a UTF-8 file and rejected by every JSON parser when it appears at the very start, because it is not whitespace as far as the JSON grammar is concerned.

# Python — the "sig" codec strips a BOM if present, and is harmless if absent
data = json.load(open('file.json', encoding='utf-8-sig'))

# JavaScript
if (text.charCodeAt(0) === 0xFEFF) text = text.slice(1);

# Shell — check for one
head -c 3 file.json | xxd   # "efbbbf" confirms a BOM

6. Comments

JSON has no comment syntax; neither // nor /* */ is legal anywhere in the document. If you control the format and want comments, JSON5, JSONC or YAML exist for exactly this reason — fighting JSON’s strictness in a hand-edited file is a losing game.

7. Smart quotes pasted from a document

Word, Google Docs, Slack and iOS all substitute curly quotes ( U+201C, U+201D) for straight ones automatically. They render close enough to " to be invisible on screen, and every JSON parser rejects them. This is one of the few triggers where the visible text and the actual bytes genuinely disagree — the fix is retyping the quotes rather than reading harder.

8. A missing comma between values

Invalid
[1 2 3]
Valid
[1, 2, 3]

Every array element and every object member must be separated by exactly one comma — not whitespace alone. This is a common artefact of hand-editing a long array and forgetting a separator when adding a line.

Worked example

A configuration file edited by hand in a rich-text-aware editor picks up curly quotes without anyone noticing:

{“timeout”: 30, “retries”: 3}

In Firefox this throws JSON.parse: expected property name or '}' at line 1 column 2 of the JSON data — not the generic “unexpected character” wording, because the parser is specifically expecting a property name at that position and can name it. In Chrome/Node it is Expected property name or '}' in JSON at position 1. Pasting it into the tool above marks the exact curly quote and names it, which on screen looks identical to a straight one:

{"timeout": 30, "retries": 3}

What to check, in order

  1. Paste the document above; the caret and code-point name settle ambiguous-looking characters (curly quotes, BOM, non-breaking space) in seconds.
  2. Check whether the document was ever opened or edited in a word processor, chat client or PDF — the most common source of invisible substitutions.
  3. Check whether it was generated by string concatenation, a template, or a language other than the one that will parse it (Python’s repr/str output being the classic case).
  4. Run head -c 3 file.json | xxd if position 0/column 1 is implicated and the file looks fine to the eye.

Why Firefox collapses so many triggers into one message

SpiderMonkey’s parser reports a specific expectation — a property name, a comma, a closing bracket — whenever it can name one precisely for the grammar position it is in. “Unexpected character” is what remains once none of those more precise messages apply: the parser was simply looking for the start of any valid JSON value or token and found something that cannot begin one, with nothing more specific to say about what it expected instead. That is why a bare NaN, a bare single-quoted string, a leading BOM and an opening /* comment all collapse into the identical wording in Firefox even though a human reader would describe them as four unrelated mistakes — structurally, to the parser, they are the same situation: an illegal character where a value was expected, and no narrower category to report.

Quick reference

What you seeMost likely cause
unexpected character … column 1Something invalid at the very start — BOM, comment, bare NaN/Infinity, single quote
expected property name or '}'Unquoted key, or a smart quote where a key should start
expected double-quoted property nameTrailing comma before a closing brace
Single quotes (') are not allowed in JSONSafari's specific wording for cause 1
Unexpected comma at the end of array expressionSafari's specific wording for a trailing comma in an array
Unrecognized token 'X'Safari's generic catch-all, roughly Firefox's "unexpected character"

Limitations and edge cases

  • Firefox’s generic wording can mean several different underlying triggers — single quotes, a bare NaN, a BOM, a comment, and a curly-quote-in-a-bare-string-context can all surface as “unexpected character.” The table above narrows it down by what you actually typed, but the tool’s own diagnosis is the fastest way to be certain.
  • Column numbers count differently across engines. Firefox and Safari report line and column; current Chrome/Node report both a character offset and a line/column; older Chrome/Node report only the character offset. All three are counting the same document, just presenting the position differently.
  • Bun uses JavaScriptCore, so its JSON.parse messages match Safari’s column in the table above, not V8’s, even though Bun is a server-side runtime most people associate with Node.
  • Deno and current Node share V8 with Chrome, so their wording tracks the “V8, current” row exactly, including across Node version upgrades that bump the underlying V8 release.

Frequently asked questions

Why does the same broken JSON produce different error text in different browsers?

Because JSON.parse is specified by behaviour — what is valid and what result a successful parse produces — not by the exact wording of a failure. Chrome, Firefox and Safari each ship their own independently written JSON parser (V8, SpiderMonkey and JavaScriptCore respectively), and each is free to phrase a rejection however its authors chose to.

Is Firefox’s message less informative than Chrome’s?

Not in general — Firefox reports a line and column for every failure, which Chrome only added in its 2023 message overhaul. Firefox’s “unexpected character” wording is simply more generic in the specific cases where it cannot name a more precise grammar expectation, which is a design choice, not a capability gap.

My error says “JSON Parse error” with no further detail at all. Which engine is that?

JavaScriptCore — Safari on any platform, or Bun. Several of its messages are noticeably terser than the other three engines’, sometimes omitting a position entirely. Paste the document above for a caret and code-point name regardless of which engine produced the original error.

Does Node always match Chrome’s wording?

Yes when both are on a current version — they share the V8 engine. An older Node release paired with a current Chrome (or vice versa) can disagree, because V8’s JSON error messages changed substantially in the 2023 release that added line/column reporting. Check both versions if the wording looks unfamiliar.

Can I make my own code produce one consistent message regardless of browser?

Yes — write your own thin wrapper that catches whatever JSON.parse throws and rethrows a message you control, including the input snippet and your own position-finding logic rather than relying on the native message text at all. That is the same approach this page’s embedded tool takes.

Is a curly quote really that hard to spot?

Yes, deliberately so — that is why word processors and chat apps substitute it automatically, to look better typeset. At most font sizes and in most editors and " are close to indistinguishable on screen. The tool above names the exact Unicode code point, which ends the guessing.

Is it safe to paste a failing production payload into this page?

Into this one, yes: there is no upload endpoint, no storage and no share link, and every check runs in your tab. That is not true of hosted formatters generally — in November 2025 two of the most widely used online code-formatting utilities were found to have been exposing years of stored submissions, credentials included. The details, with sources.

Why does Firefox say “unexpected character” for so many different mistakes?

Because it is SpiderMonkey’s fallback wording for any position where the parser is looking for the start of a legal JSON value or token and finds something that cannot begin one, with no narrower grammar expectation available to name. A bare NaN, a leading BOM, a single-quoted string and an opening comment are different mistakes to a human but the identical situation to the parser — an illegal character where a value belonged — which is why they all collapse into one message. Chrome and Safari draw that line in slightly different places, which is why the same input can look more specific in one and more generic in another.

Related pages

Related tools