Skip to content
JSON2026-08-29Begin8 min read

JSON Troubleshooting Handbook: From Error Message to Fix (Symptom Index)

Unexpected token } in JSON at position 247.

Nearly every developer has seen this. It tells you something is wrong near character 247 — but not what is wrong. And JSON errors are notoriously indirect: the actual mistake usually sits dozens of characters before the reported position.

This handbook is organized by symptom, so you can classify the problem first, then jump to the fix. Each category links to a dedicated deep-dive article; this page gives you the decision path and the minimum action to unblock.

Locate your symptom first (diagnostic table)

What you seeMost likely causeJump to
Unexpected token < or Unexpected token oResponse isn't JSON (HTML error page / undefined)Type 1
Unexpected token } / ] / ,Trailing comma, extra bracketType 1
Unexpected end of JSON inputTruncated data, empty stringType 1
Chinese becomes \uXXXXASCII escaping enabled during serializationType 2
Chinese shows as ???? or é”±Encoding mismatch (UTF-8 vs GBK)Type 2
is not valid JSON / Schema validation failsMissing required field, wrong typeType 3
Two JSONs look identical but compare unequalKey order, whitespace, number precisionType 4

Can't tell? Paste it into the JSON formatter. It highlights the first syntax error and its position — far faster than reading stack traces.


Type 1: Syntax errors (JSON.parse fails)

JSON syntax is far stricter than JavaScript object literals. That's the root of most errors.

Strictness comparison

SyntaxJavaScriptJSONNotes
Quoted keysOptionalRequired, double quotes{name:"a"} is invalid
String quotesSingle or doubleDouble quotes only{'a':1} is invalid
Trailing commaAllowedForbidden{"a":1,} is invalid
CommentsAllowedNot supported// and /* */ both fail
undefined / NaNAllowedNot valid valuesUse null
Functions, Date objectsAllowedNot supportedDates must be strings

Three frequent traps

Trap 1: the server didn't return JSON

SyntaxError: Unexpected token < in JSON at position 0

An error at position 0 almost always means the body is an HTML error page (starting with <) or plain text. Common causes: 404/500 responses, expired session redirecting to a login page, or a gateway error page.

Debug it: log the raw response body before calling JSON.parse().

Trap 2: BOM header

JSON with a UTF-8 BOM carries an invisible \uFEFF at the start, producing Unexpected token .

# Check whether a file has a BOM
head -c 3 file.json | xxd | head -1   # efbbbf means BOM present

Trap 3: truncated data

Unexpected end of JSON input usually means: empty string, null, or the transfer was cut short (large response timeout, stream not fully read).

👉 Full error list with fixes: JSON.parse Unexpected token — complete fix guide.


Type 2: Chinese & encoding issues

These are the most confusing, because the data is syntactically valid — it just looks wrong.

Symptom A: Chinese becomes \uXXXX

{"name":"\u5f20\u4e09"}

This isn't an error — it's Unicode escaping, a legal JSON representation that decodes back to "张三". It appears because serialization was configured with ensure_ascii=True (Python) or equivalent.

If you want literal Chinese characters in the file:

# Python: disable ASCII escaping
json.dumps(data, ensure_ascii=False)
// JavaScript: JSON.stringify does not escape Chinese by default
JSON.stringify({name: "张三"})  // {"name":"张三"}

👉 See Chinese turning into \uXXXX escapes.

Symptom B: mojibake (???? or é”±)

This is a character-encoding mismatch, unrelated to JSON syntax:

Garbled formCause
????Written with an encoding that can't represent Chinese (ASCII, Latin-1)
é”±UTF-8 bytes interpreted as GBK
锟斤拷UTF-8 → GBK → back again; irreversible

Golden rule: the JSON spec defines UTF-8 as the default encoding. Use UTF-8 on both read and write sides and mojibake disappears.

# Check a file's actual encoding
file -I data.json
# Convert GBK -> UTF-8
iconv -f GBK -t UTF-8 data.json > data.utf8.json

Type 3: Structure & Schema validation failures

Syntactically valid, but structurally wrong.

Common validation failures

ErrorCause
is a required propertyMissing required field
is not of a type 'string'Type mismatch (number written as string, etc.)
additionalProperties not allowedField not defined in the Schema
does not match patternFails the regex constraint

Frequent gotcha: 1 and "1" are different types in JSON. An API returning "age": "30" fails a Schema expecting integer.

Debug it: run the JSON Schema validator to get the complete error list — it reports every problem, not just the first.

👉 See JSON Schema required / type validation failures.


Type 4: Data diff & comparison

Two JSONs that "look the same" but compare unequal usually differ because of:

  1. Key order — JSON objects are semantically unordered, but string comparison is order-sensitive.
  2. Whitespace / indentation — serialization formatting differences.
  3. Number precision — floating point (0.1 + 0.2 !== 0.3), or integers beyond IEEE 754 safe range (a rounded snowflake ID shows up in Diff as “numbers differ” — the parser corrupted them, the business did not).
  4. Array order — arrays are ordered; a different order means different data.

Do it right: compare semantically (parse, then compare recursively), never as strings. The JSON Diff tool shows the differing nodes across both trees. If you suspect rewritten integers, run the lossless JSON Formatter path first, or read JSON integer precision trap, then Diff.


The five questions that localize any JSON bug

Before touching data, answer these in order — each answer eliminates half the search space:

  1. Does it fail at parse time or at use time? (Syntax vs structure — Type 1 vs Type 3)
  2. What does the first divergence character look like? (Paste both sides into a diff; the position of the first mismatch localizes the layer: transport, encoding, or generation)
  3. Whose serializer produced it? (Language dialects differ on trailing commas, large-number precision, Unicode escapes)
  4. Did it pass through anything that "helps"? (Proxies, "smart quotes" editors, Excel re-saving — each mangles in a known signature)
  5. Does the minimal reproduction still fail? (Cut the document to the failing subtree; 90% of "mystery" bugs die here and become obvious ones)

General debugging workflow (works for any JSON problem)

Four steps that resolve the vast majority of cases:

1. Inspect the raw bytes, not the parsed result Print the raw string before JSON.parse(). Many "JSON errors" aren't JSON at all (HTML error page, empty response, truncated stream).

2. Let a tool point at the first error Paste into the JSON formatter. Syntax errors get highlighted with row/column — remember the real mistake is usually before the reported spot (a missing quote only surfaces at the next token).

3. Bisect to narrow it down For large files, split the JSON in half and test each half. Repeat a few times to isolate the offending field.

# Quick command-line validation
python3 -m json.tool data.json > /dev/null && echo "Valid JSON" || echo "Invalid JSON"
# or
jq . data.json > /dev/null && echo "Valid JSON"

4. Re-validate after fixing If the API still errors once syntax passes, the problem is structure, not syntax — move to Schema validation.


Troubleshooting toolkit

NeedTool
Format / highlight syntax errorsJSON formatter
Diff two JSON documentsJSON Diff
Validate structure against a SchemaJSON Schema validator
Extract fields from large JSONJSONPath query
JSON to Java / Go / TS classesTo Java · To Go

Every tool above runs locally in your browser — safe to paste production JSON into. Open the Network panel (F12) and verify it yourself.


FAQ

Q: Why does the error position usually point past the real mistake? Because the parser only notices something is wrong when it reaches a later character. In {a:1}, the unquoted a isn't flagged until the parser hits the colon or the next token. The reported position is where the problem surfaces, not where it started.

Q: JSON.parse succeeded but the data is wrong — why? Valid syntax doesn't mean correct semantics. Common cases: numbers stored as strings, wrong nesting level, an array serialized as an object, large-integer precision loss. Catch these with Schema validation or a diff.

Q: How do I debug very large JSON (hundreds of MB)? Browser tools hit memory limits. Use the command line: jq for streaming and locating, python3 -m json.tool for validation. Start with jq 'keys' to see the top-level shape, then drill down.

Q: How do I stop JSON problems from recurring? Add a CI gate: validate API responses against a Schema and snapshot-diff them. That catches most structural changes before merge.

Summary

The core approach: classify first, then drill down.

  1. Syntax error → look before the reported position; check quotes, commas, comments
  2. Chinese issue → decide whether it's Unicode escaping (legal) or mojibake (encoding mismatch)
  3. Structure issue → use a Schema validator for the full error list
  4. Diff issue → compare semantically, never as strings

Bookmark this handbook. Next time you hit Unexpected token, match your symptom against the table instead of counting characters.

Implementation notes

A concrete example of the handbook's advice to compare diffs semantically: the diff parser runs in lossless mode, where big integers of equal value compare by their original text and unequal object references do not count as a difference. Otherwise two long IDs with the same numeric value would be reported as a modification — a false diff that makes you suspect perfectly healthy data.