Fix · Unexpected token

"Unexpected token in JSON" — What It Means and How to Fix It

The most common JSON error, decoded: what the parser is actually complaining about, how to read the token and position, and how to fix each cause. The validator below is preloaded with a broken sample so you can watch it fail.

No sign-up to start · Runs in your browser

JSON Validator
Open in full editor
Input
Tree
Valid JSON will appear here as a collapsible tree.

Runs entirely in your browser — nothing is uploaded.

{"name": "Ada", "role": admin, "tags": ["read" "write"]}
SyntaxError: Unexpected token means JSON.parse hit a character the JSON grammar forbids at that spot. At position 0, the input usually is not JSON at all — an HTML error page or an empty response body. Mid-document, it is almost always quotes or commas: an unquoted value, a single-quoted string, a missing or trailing comma. "Unexpected end of JSON input" means the document was cut off. The validator above is preloaded with a broken sample; paste your own JSON to see the exact failing character.

The broken sample, decoded

Broken sample (preloaded in the validator above)
{"name": "Ada", "role": admin, "tags": ["read" "write"]}
Validator output, fix by fix (Chrome, Edge, Node)
Unexpected token 'a', ...", "role": admin, "ta"... is not valid JSON

→ quote the value ("role": "admin") and validate again:

Expected ',' or ']' after array element in JSON at position 49 (line 1 column 50)

→ add the comma (["read", "write"]) and validate again:

Valid JSON — no syntax errors

Two bugs, reported one at a time, because parsers stop at the first illegal character. The a of admin sits at index 24 — older Chrome and Node versions phrased the same failure as "Unexpected token a in JSON at position 24", Firefox says line 1 column 25, and Safari flags the identifier instead. Once admin is quoted, the parser reaches the missing comma between "read" and "write", and modern V8 names the exact fix it expects. "Valid JSON — no syntax errors" is the validator's live status once both are fixed.

How to fix an unexpected token error

  1. 1

    Paste the exact failing text

    Copy the precise string your code passed to JSON.parse — from a log or a res.text() dump, not what you think the API sent. Pasting a pretty-printed copy shifts every position the parser reports.

  2. 2

    Read the token and the position

    The token is the character the parser rejected; the position is a zero-based index into the raw string, whitespace included. The real mistake sits at that character or just before it.

  3. 3

    Rule out the position-0 case

    An error at position 0 — typically token < — means the input is not JSON at all, usually an HTML error page or an empty body. Log res.status and await res.text() before parsing to see what actually arrived.

  4. 4

    Fix quotes, commas, and bare values

    Mid-document errors are almost always one character: quote the bare value, add the missing comma, delete the trailing one, or replace single quotes with double quotes. Re-validate after each fix — parsers report one error at a time.

  5. 5

    Repair truncation automatically

    If the error is "Unexpected end of JSON input", run the text through JSON Repair: it closes unfinished strings and brackets deterministically and lists every fix it applied.

The usual suspects

Position 0: not JSON at all

An error on the very first character means the body is HTML (an error page, a login redirect, an SPA fallback) or empty. Inspect the raw response before blaming the JSON.

Unquoted keys and values

{name: "Ada"} and "role": admin are legal JavaScript but illegal JSON — every key and every string value needs double quotes.

Wrong quotes

Single quotes and the curly smart quotes that word processors and chat apps insert both break strict parsers. JSON accepts straight double quotes only.

Comma trouble

A missing comma between items (["read" "write"]) or a trailing comma before } or ]. Modern V8 even names the missing comma: Expected ',' or ']' after array element.

Truncated input

"Unexpected end of JSON input" means the document stopped mid-value: capped log lines, proxy size limits, streams read too early, LLM output that hit its token limit.

Non-JSON literals

NaN, undefined, Infinity, and Python's True/None sneak in when objects are stringified by hand. JSON.stringify never emits them — NaN and Infinity become null; json.dumps fixes True/None but still writes NaN unless you pass allow_nan=False.

Anatomy of the error message

Every variant of the error carries up to three clues: the token (the exact character the parser rejected), the position (a zero-based index into the string you passed to JSON.parse), and, in newer engines, a line and column. The wording depends on the JavaScript engine, not on your code. Modern V8 — Chrome, Edge, Node 20+ — quotes the token with a context snippet, which is what the validator above shows for the preloaded sample. Older V8 said "Unexpected token a in JSON at position 24". Firefox reports "at line 1 column 25 of the JSON data", and Safari names the identifier it stumbled over. Same failure, four spellings — never string-match on error text.

Two things trip people up. First, the position is where the parser gave up, not always where the bug lives: a quote that was never closed turns everything after it into one long string, so the failure gets reported far downstream of the real mistake. When the position looks nonsensical, scan backwards for an unbalanced quote. Second, the position indexes the exact string your code received — including every space and newline — not a pretty-printed copy of it.

Parsers also stop at the first illegal character, so a document with several mistakes reports exactly one error. The sample on this page demonstrates it: fix the unquoted admin and a second error appears at the missing comma. Fix, re-validate, repeat — the loop takes seconds in the validator above.

Position 0 means you never got JSON

"Unexpected token < in JSON at position 0" is the most-searched variant for a reason: the body starts with <, which means HTML. The usual producers are a 500 or 502 error page from a proxy, a login page served after an expired session (the API redirected instead of returning a 401 with a JSON body), and an SPA rewrite answering a mistyped API path with index.html. The empty-body twin is "Unexpected end of JSON input" thrown on the very first parse: a 204 No Content, an empty reply from a crashed handler, or res.json() called on a response that never had a body.

The debugging recipe is one line: read the body as text before you parse it. const text = await res.text(); console.log(res.status, res.headers.get("content-type"), text.slice(0, 200)) shows instantly whether JSON ever arrived — then call JSON.parse(text) yourself. Note that res.json() and res.text() both consume the body stream, so you cannot log the text after res.json() has already failed. That is exactly why the blind res.json() call makes this error so opaque.

Once you can see the real payload, the fix lives in the request or the server, not in the JSON: correct the URL or base path, send the missing auth header, make the API return JSON error bodies with proper status codes, or point the dev proxy at the right port.

Mid-document errors: quotes, commas, bare values

When the position lands inside the document, the cause is nearly always one of five patterns. Single quotes: {'name': 'Ada'} is valid JavaScript, invalid JSON. Unquoted keys: {name: "Ada"} — same story. Unquoted values: "role": admin, the first bug in the sample above. Missing commas: ["read" "write"], the second bug — modern V8 even spells out the repair, "Expected ',' or ']' after array element". Trailing commas: [1, 2, 3,] before a closing bracket.

A second family comes from serializing by hand. NaN, Infinity, and undefined leak out of JavaScript string concatenation; True, False, and None leak out of Python when someone ships str(dict) instead of json.dumps. Smart quotes arrive via copy-paste from Word, Google Docs, or Slack and look identical to straight quotes until a parser rejects them. One rule prevents the entire class: never build JSON by concatenating strings — always emit it with JSON.stringify or json.dumps.

Part of this class is automatable. JSON Repair deterministically converts single and smart quotes, quotes bare keys, strips trailing commas and comments, translates Python and JavaScript literals, and removes markdown fences — reporting each fix it applied. What it will not do is guess: a bare value like admin (should it be "admin", or did a variable fail to interpolate?) and a missing comma between two closed strings are ambiguous, so repair re-throws the parse error instead of inventing data. For those two, the validator points at the character and the fix is one keystroke.

"Unexpected end of JSON input": the truncation case

This message means the parser ran out of characters while something was still open — a string missing its closing quote at the very end, an array missing its ], an object missing its }. The usual producers: log pipelines that cap line length, proxies and gateways with response-size limits, curl output interrupted by a timeout, a copy-paste that missed the tail, a streamed fetch body parsed before the stream finished, and LLM responses that hit their token limit mid-object.

Truncation is the one case worth automating. JSON Repair closes unfinished strings and brackets deterministically — no AI involved, same output every time — and tells you what it did: feed it {"user": "Ada", "scopes": ["read", "wri and it returns valid JSON with the status "Repaired — closed unclosed brackets". The caveat is honest: repair restores syntax, not data. Whatever was cut off is gone, so treat the repaired document as a way to inspect what survived, and re-fetch or re-export the source when you need the complete payload.

To stop it recurring: compare Content-Length against the bytes you actually received, await the complete body before parsing, raise the size limit on whichever proxy clipped the response, and for LLM output either raise max tokens or request smaller chunks. Once the JSON parses, the Open in full editor button carries it into the editor for tree view, formatting, mind maps, and AI-assisted cleanup.

Frequently asked questions

What does "Unexpected token in JSON at position 0" mean?

The very first character was already illegal, so the input is not JSON at all. Nine times out of ten it is an HTML error page (the token is <), a login redirect, or an empty body. Log res.status and await res.text() before parsing to see what actually arrived.

What does the position number mean?

It is a zero-based character index into the exact string you passed to JSON.parse — position 24 is the 25th character, counting every space and newline. Modern Chrome and Node add a line and column too; the validator on this page finds the spot for you.

Why do Chrome, Firefox, Safari, and Node word the error differently?

The message text is not standardized — each JavaScript engine writes its own. V8 (Chrome, Edge, Node) quotes the token with a context snippet, Firefox reports line and column, Safari names the identifier it hit. Never branch on error strings; catch the exception and log the raw input instead.

What causes "Unexpected end of JSON input"?

The parser reached the end of the string while a string, array, or object was still open — the document was truncated, or you parsed an empty body. Typical sources: capped log lines, proxy body-size limits, streamed responses read too early, and LLM output that hit its token limit.

Can the error be fixed automatically?

The mechanical class, yes: JSON Repair deterministically fixes single and smart quotes, unquoted keys, trailing commas, comments, Python and JavaScript literals like True or NaN, markdown fences, and truncated endings — and lists every fix it applied. Genuinely ambiguous mistakes, like the bare value admin or a missing comma between two strings, still need a one-character manual fix.

Why does fixing one error just reveal another?

JSON parsers stop at the first illegal character and report nothing beyond it, so a document with five mistakes produces one error at a time. Fix the reported character, re-validate, and repeat until the status turns green — usually a few quick rounds.

Is it safe to paste a production API response here?

The validator runs entirely in your browser: the text is parsed locally with JSON.parse and never uploaded or stored on a server. JSON Repair works the same way. Still, redact tokens and secrets before sharing screenshots of the result.

Why does JSON.parse reject syntax that JavaScript itself accepts?

JSON is a strict subset of JavaScript literal syntax. Single quotes, unquoted keys, trailing commas, comments, NaN, and undefined are all legal in a JS object literal but illegal in JSON. If you built the string by hand, switch to JSON.stringify (or json.dumps in Python) and this whole error class disappears.

Related tools

Find your unexpected token

Open the Editor