Common JSON syntax errors and how to fix them
Trailing commas, single quotes, unescaped characters, BOMs, and NaN. The JSON parse errors developers actually hit, what causes them, and the exact fix for each.
JSON has one of the smallest grammars in wide use. It also produces some of the least helpful error messages, because most parsers report where they gave up rather than where you went wrong. Those are frequently different places.
Here are the errors that come up most, what actually causes them, and how to fix each one.
Unexpected token } in JSON at position N
Almost always a trailing comma.
{
"name": "api",
"port": 8080,
}
JavaScript object literals allow that final comma. JSON does not, and neither does any strict parser. Same rule applies inside arrays: [1, 2, 3,] is invalid.
The fix is to delete it. The complication is that the reported position points at the closing brace, not the comma, so on a large file you are looking one token behind wherever the parser stopped.
Unexpected token ' in JSON at position N
JSON strings require double quotes. Single quotes are not a valid string delimiter anywhere in the format, for keys or values.
{ 'name': 'api' }
Both quote pairs need to become ". This one shows up constantly when someone pastes a Python dict or a JavaScript object literal and expects it to parse as JSON. They look nearly identical and are not the same thing.
Unexpected token N in JSON at position N
You have NaN, Infinity, or -Infinity in the document. These are valid JavaScript number values but they do not exist in JSON. The spec has no way to represent them.
{ "ratio": NaN }
You have to decide what they should be instead: null, a string like "NaN", or omit the field. This usually originates upstream, where something serialized a division result without checking it, so fixing the producer matters more than fixing the file.
Unexpected token u in JSON at position 0
The literal string undefined reached your parser. Position 0 means it is the entire payload, not a field inside it.
This is nearly always a bug one layer up: an API returned nothing, something called JSON.parse(undefined), or a variable was stringified before it was assigned. The JSON is not the problem. Check what produced the input.
Unexpected token < in JSON at position 0
You are parsing HTML, not JSON. A < at position 0 is the start of <!DOCTYPE html> or <html>.
This means a fetch hit an error page, a login redirect, or a 404 that returns HTML while your code assumed JSON. Log the raw response body before parsing and you will usually see a server error page.
Unexpected token in JSON at position 0 with a file that looks fine
Suspect a byte order mark. A UTF-8 BOM is three invisible bytes (EF BB BF) at the start of the file that some Windows editors add on save. Your eyes see {; the parser sees something before it.
Re-save the file as "UTF-8 without BOM". In an editor that shows encodings, this is usually a dropdown next to the save dialog.
Bad control character in string literal
A raw newline, tab, or other control character sits inside a string. JSON string values cannot contain literal control characters, they have to be escaped:
{ "query": "SELECT *\nFROM users" }
The \n there is a two-character escape sequence, not an actual line break in the file. Same for \t, and for \\ when you want a literal backslash. Windows file paths are a frequent offender: "C:\Users\api" is invalid because \U and \a are not valid escapes.
Duplicate keys that silently do nothing
{ "port": 8080, "port": 9090 }
This parses without error in most implementations, and last-write-wins, so you get 9090. No parser tells you the first value vanished.
This one is genuinely dangerous because there is no error at all, just a config value that quietly is not what you think it is. A validator that flags duplicates catches it; the base parser will not.
Comments
JSON has no comments. Not //, not /* */, not #.
{
// the port to bind
"port": 8080
}
Some tools accept these as an extension (JSON5, JSONC, and tsconfig.json specifically), but a standard parser rejects them. If you need annotated config, either use a format that supports comments, such as YAML or TOML, or add a real "_comment" field and accept that it ships as data.
Working through them faster
Two things speed this up considerably.
First, use a parser that reports line and column, not just a byte offset. Counting to "position 4,721" by hand is miserable. The JSON Validator points at the line.
Second, once it parses, run it through JSON Schema validation if you have a schema. Syntactic validity and correctness are different questions: {"port": "8080"} is perfectly valid JSON and still wrong if your service expects a number.
For the same treatment across SQL, YAML, JWTs, Dockerfiles, and the rest, there is a full error reference covering every tool on the site.