How to fix JSON parse errors: checking Unexpected token step by step
Bottom line: Turn JSON.parse errors into a line and character, then rule out six causes: trailing commas, single quotes, comments, control characters, BOM and truncation.

Bottom line: on a parse error, read the reported line and column to find the first illegal character, then decide whether it is a missing quote, a missing comma, a trailing comma, a BOM, or truncation — do not re-read the whole document from the start.
An error message never tells you "what is wrong"; it only tells you "where the parser could no longer proceed". Those two positions are often not the same place.
1. First, translate the error into coordinates
The same broken JSON produces completely different errors in different languages, but all of them can be converted into "which line, which character":
| Environment | Typical error | How to read it |
|---|---|---|
| Browser / Node | Unexpected token } in JSON at position 42 |
position is a 0-based character offset, not a line number |
| Node 20+ | Unexpected non-whitespace character after JSON at position 42 |
means the first part is valid and the extra content starts after 42 |
| Java Jackson | Unexpected character ('}' (code 125)): was expecting ... at [Source: (String)..."..." ; line: 3, column: 1] |
gives line and column directly, the most usable of all |
| Python | json.decoder.JSONDecodeError: Expecting ',' delimiter: line 3 column 1 (char 42) |
char is the offset; line and column are what matter |
| Go | invalid character '}' looking for beginning of object key string |
gives only the character, so you have to backtrack and locate it yourself |
Once you have the position, paste the JSON into the JSON online parser; the tool places the cursor at the error location and highlights the line and column number — this step saves 90% of eyeballing characters.
2. Six high-frequency errors
1. Trailing comma
{
"name": "json",
"version": 1,
}
There is an extra comma before }. The JSON specification does not allow trailing commas, while JavaScript object literals do, so configuration copied out of code is the most likely to carry one. Fix: delete the last comma; if this data lives in a JS file to begin with, switch to JSON5 conversion instead of forcing it into JSON.
2. Single quotes and unquoted keys
{ 'name': 'json' }
{ name: "json" }
JSON strings may only use double quotes, and keys must be double-quoted strings as well. These two are the relaxed syntax of JSON5 / JavaScript, not JSON. A double quote inside a string must be written as \", and a backslash as \\.
3. Comments
{
// Timeout in milliseconds
"timeout": 3000
}
The grammar of RFC 8259 has no place for comments. Either delete the comments, or save as .jsonc / .json5 and use a parser that supports comments. For the trade-offs among the various "comment schemes", see How to write comments in JSON.
4. Raw newlines and control characters inside strings
{"sql": "SELECT *
FROM t"}
JSON strings may not contain raw line feeds, tabs, or the control characters U+0000–U+001F; they must be written as \n, \t, \u0009. When stuffing multi-line text into JSON, escape it once in an editor first, or handle it uniformly with JSON minify and escape.
5. BOM header
UTF-8 saved by Windows Notepad carries the three bytes EF BB BF. JSON.parse reports Unexpected token when it encounters a BOM, and the eye cannot see it at all. How to tell: inspect the file header in hex, or copy the content into an online tool and see whether the first character displays as . Fix: save as "UTF-8 without BOM".
6. Truncated content
A log that printed only the first 4096 characters, an API response cut off by a gateway, an interrupted download — the hallmark of this class is "the error is at the end" and the bracket counts do not match. Count brackets first: if the closings of { and [ do not match, it is truncation, not a syntax error. In that case do not fix anything — go get the full data.
3. Two false alarms involving Chinese text and escaping
Chinese turns into \u4e2d\u6587: this is valid JSON escaping, not garbled text. After parsing, the value is restored to Chinese; the \u form only shows when you look at the raw string directly. When you need to show it to people, just output it with the formatting tool; you only need to care about escaping strategy when Chinese must survive in a URL or a log.
Chinese turns into garbled text like 涓枃: this is an encoding problem, not a JSON problem. It happens when UTF-8 bytes are decoded as GBK, producing this regular pattern of "three characters standing in for one Chinese character". Check the response header's Content-Type: application/json; charset=utf-8, and check whether encoding='utf-8' is specified explicitly when reading the file.
4. A five-minute repair workflow
- Paste it into the tool: first use the online parser to locate the first error, and fix only one at a time;
- Validate after each fix: re-parse after every change, so that fixing several places at once does not let new errors mask the old one;
- Minify once: strip whitespace with Minify and parse again; this distinguishes "structural errors" from "whitespace or newline problems";
- Compare structure: if an error suddenly appears after a change, use JSON diff against the previous version; the difference is usually that one comma;
- Field-level validation: if the syntax passes but the business layer rejects it (missing field, wrong type), that falls under JSON Schema validation, not a syntax problem.
5. Why one of two "identical-looking" JSON snippets parses and the other does not
The most common cause is invisible characters: the full-width comma ,, full-width quotes “”, the non-breaking space U+00A0, the zero-width space U+200B. On screen they are nearly indistinguishable from their half-width counterparts, but none of them is part of JSON grammar.
There is only one way to tell: copy the content around the offending character and inspect the code points byte by byte. Running the following in a browser console lists every character that is non-ASCII and outside the Chinese ranges:
[...text].filter((ch) => {
const code = ch.codePointAt(0);
return code > 127 && !(code >= 0x4e00 && code <= 0x9fff);
});
If 0x201c (left double quote), 0xff0c (full-width comma), or 0x00a0 shows up in the output, they are the culprit.
6. Prevention is cheaper than repair
- Run a parse check on configuration files before committing; one line in CI is enough:
node -e "JSON.parse(require('fs').readFileSync('config.json','utf8'))"; - Generate API responses with a serialization library; never hand-concatenate strings — hand-built strings are the only source of trailing commas and missing quotes;
- When logging JSON, call
JSON.stringifyfirst instead of interpolating it into a template, so that a half-written payload is not mistaken for a complete message.
FAQ
How do I locate the position of a JSON syntax error quickly?
Take the position or line/column the parser reports; position is a 0-based character offset, not a line number, while Java Jackson and Python report the line and column directly. Paste the JSON into the JSON online parser and the tool puts the cursor at the error location and highlights the line and column. If the error sits at the start, check the file header for a BOM and look for invisible characters.
Why is configuration copied from code so likely to carry a trailing comma?
The JSON specification forbids trailing commas while JavaScript object literals allow them, so configuration copied out of JS code easily brings one along. The fix is simply to delete the last comma; if the data belongs in a JS file anyway, switching to JSON5 conversion is a better fit than forcing it into JSON.
Can I parse a configuration file that contains comments as regular JSON?
No — the grammar of RFC 8259 has no place for comments, so parsing it directly raises an error. Either delete the comments, or save as .jsonc / .json5 and use a parser that supports comments. For the trade-offs among comment schemes, see How to write comments in JSON.