JSON parsers are strict on purpose. A document is either completely valid or it is rejected; there is no partial result and no "best effort" recovery. The grammar in RFC 8259 is tiny, and every conforming parser must refuse anything outside it, so a single stray comma in {"name": "Ada",} is enough to fail the whole file. That strictness is what makes JSON safe and fast to parse, but the error messages differ from tool to tool and often point at where the parser gave up rather than at the actual mistake.
This guide lists the errors you are most likely to meet, shows how each parser reports them, and gives the fix. If you want the syntax rules first, start with JSON format explained.
How different parsers report errors
The same broken input, {"a": 1, "b": 2,}, produces a different message everywhere:
| Environment | Message |
|---|---|
Chrome, Edge, Node.js (JSON.parse) |
SyntaxError: Unexpected token '}', ..."b": 2,}" is not valid JSON (older versions: Unexpected token } in JSON at position 16) |
| Firefox | SyntaxError: JSON.parse: expected double-quoted property name at line 1 column 17 of the JSON data |
Python (json.loads) |
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 17 (char 16) |
.NET System.Text.Json |
System.Text.Json.JsonException: The JSON object contains a trailing comma at the end which is not supported in this mode. Change the reader options. Path: $ \| LineNumber: 0 \| BytePositionInLine: 16. |
PHP (json_decode) |
Returns null; json_last_error_msg() returns Syntax error |
Go (encoding/json) |
invalid character '}' looking for beginning of object key string |
| VS Code / GitHub editor | Trailing comma (and Expected a JSON object, array or literal when the file does not start with a value) |
Note the position conventions: JavaScript reports a zero-based character offset, Python reports both a one-based line and column and a zero-based char offset, and .NET's LineNumber is zero-based.
"Expected a JSON object, array or literal"
This wording comes from the JSON language service behind Visual Studio Code, and from editors that reuse it such as GitHub's web editor. It means the editor is treating the file as JSON but the first non-whitespace thing it finds is not a JSON value. The same situation produces Unexpected end of JSON input (empty input) or Unexpected token '<', "<!DOCTYPE "... is not valid JSON in JavaScript, Expecting value: line 1 column 1 (char 0) in Python, and The input does not contain any JSON tokens in .NET.
Typical causes:
- Empty input. The file is zero bytes, or the variable you passed to the parser is an empty string.
- Stray text before the JSON. A log line, a PHP warning, a shell prompt, or an HTML error page. The classic case is a
fetch()call that received a 404 or login page instead of the API response:
<!DOCTYPE html>
<html><head><title>404 Not Found</title></head>...
- The file is not JSON at all. A
.jsor.mdfile opened with the JSON language mode selected.
Fix: make sure the content begins with {, [, a double-quoted string, a number, or true/false/null. In code, check response.ok and the Content-Type header before calling .json(), and log the raw text when parsing fails so you can see what actually arrived. In an editor, switch the language mode if the file was mis-detected.
Trailing commas
JavaScript, Python, C# and most other languages let you leave a comma after the last item in a list. JSON does not.
{ "a": 1, "b": 2, }
[1, 2, 3,]
Messages: JavaScript Unexpected token '}' or Unexpected token ']'; Python Expecting property name enclosed in double quotes or Expecting value; .NET The JSON object contains a trailing comma at the end which is not supported in this mode.
{ "a": 1, "b": 2 }
[1, 2, 3]
Fix: remove the final comma. If you are building JSON by string concatenation in a loop, that is where the extra comma comes from; use the language's serializer (JSON.stringify, json.dumps, JsonSerializer.Serialize) instead. .NET can be told to accept them with JsonSerializerOptions.AllowTrailingCommas = true, but that only helps if you control the consumer.
Single quotes instead of double quotes
{'name': 'Ada', 'age': 36}
Messages: JavaScript Unexpected token ''', "{'name': 'A"... is not valid JSON; Python Expecting property name enclosed in double quotes: line 1 column 2 (char 1); .NET ''' is an invalid start of a property name. Expected a '"'.
{"name": "Ada", "age": 36}
Cause: the text was produced by printing a Python dictionary (str(d) or print(d)) or by pasting a JavaScript object literal. Fix: use json.dumps(d) or JSON.stringify(obj), or replace the quotes. Note that apostrophes inside a string are fine without escaping: "it's" is valid.
Unquoted keys
{name: "Ada", age: 36}
Messages: JavaScript Unexpected token 'n', "{name: "Ad"... is not valid JSON; Python Expecting property name enclosed in double quotes; .NET 'n' is an invalid start of a property name. Expected a '"'.
{"name": "Ada", "age": 36}
Cause: JavaScript object-literal syntax, which allows bare identifiers as keys. JSON requires every key to be a double-quoted string, even if it is a single word.
Comments
{
// database settings
"host": "localhost", /* default */
"port": 5432
}
Messages: JavaScript Unexpected token '/', "{ // datab"... is not valid JSON; Python Expecting property name enclosed in double quotes; .NET '/' is an invalid start of a property name or '/' is an invalid start of a value.
Fix: delete the comments. Some files that look like JSON are actually JSONC (tsconfig.json, VS Code's settings.json) and their own tools accept comments, but a standard parser will not. If your application needs to skip comments, .NET offers JsonCommentHandling.Skip, Newtonsoft's Json.NET ignores them by default, and Node has packages such as strip-json-comments. If the file exists mainly for humans, YAML supports comments natively.
NaN, Infinity and undefined
{"score": NaN, "limit": Infinity, "note": undefined}
Messages: JavaScript Unexpected token 'N', "{"score": NaN"... is not valid JSON; .NET 'N' is an invalid start of a value; Python's default parser silently accepts NaN and Infinity as an extension, which hides the problem until another consumer chokes on it.
{"score": null, "limit": null}
Cause: these are JavaScript and Python values, not JSON values. JSON.stringify converts NaN and Infinity to null and drops object properties whose value is undefined, so the broken form usually comes from hand-built strings or from Python's json.dumps, which writes NaN unless you pass allow_nan=False. Fix: use null, use a string such as "Infinity" if the distinction matters, or omit the field.
Duplicate keys
{"id": 1, "name": "Ada", "id": 2}
This is rarely a syntax error, which makes it worse. RFC 8259 says keys "SHOULD be unique" and that behaviour with duplicates is unpredictable. JSON.parse and Python both keep the last value (id becomes 2), .NET's JsonSerializer also takes the last value when mapping to a class, while some strict validators reject the document outright. The symptom is a value that is silently different from the one you expected. Fix: remove the duplicate; the JSON Formatter will show you the value the browser's parser kept.
Wrong escaping: backslashes, control characters and smart quotes
JSON strings use the backslash for escapes, so a backslash that is not followed by one of " \ / b f n r t u is an error.
{"path": "C:\Users\ada\Documents"}
Messages: JavaScript Bad escaped character in JSON at position 13; Python Invalid \escape: line 1 column 13 (char 12); .NET 'U' is an invalid escapable character within a JSON string. The string should be correctly escaped.
{"path": "C:\\Users\\ada\\Documents"}
Raw line breaks and tabs inside a string are also forbidden; they must be written as \n and \t. Python reports Invalid control character at, JavaScript Bad control character in string literal in JSON, and .NET '0x0A' is invalid within a JSON string.
A subtler version is smart quotes. Text that has passed through Word, Outlook or a phone keyboard often arrives with curly quotation marks:
{“name”: “Ada”}
“ (U+201C) and ” (U+201D) are not the ASCII double quote " (U+0022), so the parser sees an unquoted key: JavaScript reports Unexpected token '“' and Python Expecting property name enclosed in double quotes. Fix: replace them with straight quotes. They are hard to spot by eye, so paste the text into the formatter and look at the reported column.
A UTF-8 byte order mark at the start of the file
Some Windows tools (older versions of Notepad, PowerShell 5's Out-File, some Excel exports) write a byte order mark, the three bytes EF BB BF, at the beginning of a UTF-8 file. It is invisible in most editors but shows up as  when the file is viewed as Windows-1252.
Messages: Python json.decoder.JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0); JavaScript Unexpected token '', "{"a": 1}" is not valid JSON, where the "token" is the invisible U+FEFF character.
Fix: RFC 8259 says a JSON writer must not add a BOM, so save the file as "UTF-8" rather than "UTF-8 with BOM" (VS Code shows the encoding in the status bar). When reading files you do not control, use open(path, encoding='utf-8-sig') in Python, strip a leading \ufeff in JavaScript, or let .NET's JsonSerializer.Deserialize(Stream) handle it, which skips a UTF-8 BOM. For the background, see character encoding explained.
Truncated or concatenated JSON
Truncated documents end before the closing brackets. They come from log lines that were cut at a length limit, HTTP responses that were interrupted, and terminal windows that dropped the end of a paste:
{"users": [{"id": 1}, {"id": 2
Messages: JavaScript Unexpected end of JSON input; Python Expecting ',' delimiter or Unterminated string starting at, depending on where the cut fell; .NET Expected depth to be zero at the end of the JSON payload. There is an open JSON object or array that should be closed.
Concatenated documents are the opposite problem: two or more complete values with nothing joining them, which is what you get from a log file where each line is a JSON object:
{"a": 1}{"b": 2}
Messages: JavaScript Unexpected non-whitespace character after JSON at position 8; Python Extra data: line 1 column 9 (char 8); .NET '{' is invalid after a single JSON value. Expected end of data.
Fix: for truncation, obtain the full document again and check size limits in whatever produced it. For concatenation, either parse the input one line at a time (the JSON Lines / NDJSON convention) or wrap the values in an array with commas between them: [{"a": 1}, {"b": 2}].
Numbers with leading zeros or a plus sign
{"zip": 02134, "delta": +5, "ratio": .5, "total": 1.}
JSON numbers have a narrow grammar: an optional minus, digits with no leading zero (except 0 itself), an optional fraction that must have digits on both sides of the point, and an optional exponent. Leading zeros, a + sign, a bare .5, a trailing 1., hexadecimal 0xFF and octal are all rejected.
Messages: JavaScript Unexpected number in JSON at position 9, Unexpected token '+' or Unexpected token '.'; Python Expecting ',' delimiter for 02134 (it reads the 0, then stalls) and Expecting value for +5 or .5; .NET Invalid leading zero before '2'.
{"zip": "02134", "delta": 5, "ratio": 0.5, "total": 1.0}
Fix: write 5, 0.5 and 1.0. For postal codes, phone numbers and account numbers the leading zeros are data, so store them as strings; a number type would drop them anyway.
How to find the error quickly
- Paste it into the JSON Formatter and Validator. It runs the browser's parser and shows the exact error message with the position of the failure, then pretty-prints the document once it is valid so you can inspect it as text or as a tree.
- Read the position in the message. JavaScript's
position Nis a zero-based character offset from the start of the input; Python givesline X column Y (char Z); .NET gives a zero-basedLineNumberandBytePositionInLine; Firefox gives a line and column. Remember that the reported position is where the parser stopped, which is often one token after the real mistake: a trailing comma is reported at the closing}that follows it, and a missing comma is reported at the start of the next key. - Binary-search large files. If the message is unhelpful, or the file is tens of megabytes, cut the document in half (keeping the brackets balanced), validate each half, and repeat on the half that fails. On the command line,
python -m json.tool file.jsonandjq . file.jsonboth report line numbers. - Fix the producer, not the text. If the JSON is built with string concatenation or a template, the same bug will come back. Serialize an object instead.
- Look at what you actually received.
curl -ishows the status code andContent-Type; an HTML error page arriving where JSON was expected explains mostUnexpected token '<'reports.
Once the document parses, you may find the structure itself is the issue: deeply nested objects that would be simpler as a table can be flattened with JSON to CSV (see CSV files explained), and configuration that needs comments can be converted with JSON to YAML.
Frequently Asked Questions
What does "Unexpected token in JSON at position 0" mean?
The parser failed on the very first character, so the input does not start with a JSON value at all. Common causes are an empty string, an HTML error page where an API response was expected (the token is usually <), a UTF-8 byte order mark, or a variable that holds undefined rather than a string. Log the raw text before parsing it and check the HTTP status and Content-Type of the response.
Why does JSON.parse fail on a trailing comma when JavaScript allows it?
JSON is a separate, stricter format that only resembles JavaScript object syntax. JavaScript's own grammar accepts [1, 2,] and {a: 1,}, but the JSON grammar in RFC 8259 requires every comma to be followed by another item, so JSON.parse must reject it. Remove the comma, or generate the text with JSON.stringify rather than by hand.
Can I use single quotes in JSON?
No. Both keys and string values must be enclosed in double quotes. Single-quoted text is the most common reason a Python dictionary printed with print() or a JavaScript object literal fails to parse. Use json.dumps() or JSON.stringify() to produce the text, or replace the quotes.
What does "Expecting value: line 1 column 1 (char 0)" mean in Python?
Python's json.loads found nothing it could interpret as a value at the start of the input. The usual causes are an empty string or file, a response body that is HTML or plain text rather than JSON, or a leading UTF-8 byte order mark. Print repr(text[:100]) to see exactly what the parser received, and check response.status_code if the text came from an HTTP request.
How do I fix "Unexpected end of JSON input"?
The document is incomplete: the parser reached the end of the text while at least one object or array was still open. The text was truncated somewhere, typically by a log length limit, an interrupted download, or a copy-and-paste that missed the end. Get the complete document again; if it is being streamed, wait for the whole body before parsing.
How do I validate JSON online?
Paste the text into the JSON Formatter and Validator. It parses the input in your browser, nothing is uploaded, and it reports whether the document is valid along with the parser's error message and position when it is not. Once valid, it can pretty-print or minify the JSON and show it as a collapsible tree.