An API call fails, a config file will not load, and the only clue is a message like Unexpected token } in JSON at position 142. Invalid JSON is one of the most common bugs in web development, and one of the easiest to fix once you know how to read the error. This guide walks through a repeatable debugging process and the mistakes behind almost every invalid JSON document.
Step 1: read the parser error properly
Parsers stop at the first character that cannot continue a valid document and report where that happened. The wording differs by runtime:
- JavaScript (
JSON.parse):SyntaxError: Unexpected token } in JSON at position 142in older engines, or a more descriptive message such asExpected double-quoted property name in JSON at position 142in newer ones. The position is a zero-based character offset. - Python (
json.loads):json.decoder.JSONDecodeError: Expecting ',' delimiter: line 5 column 3 (char 87). Lines and columns start at 1;charis zero-based. The exception also haslineno,colnoandposattributes. - PHP:
json_decode()returnsnull; calljson_last_error_msg(), or passJSON_THROW_ON_ERRORto get an exception with a message like “Syntax error”.
The reported position is where the parser noticed the problem, which is often just after the real mistake. A missing comma is reported at the start of the next value; a trailing comma is reported at the closing bracket after it.
Step 2: find the exact spot
Minified JSON on one line makes “position 142” hard to find. Paste the text into the JSON validator, which reports the line and column of the first error, or format it with the JSON formatter so the structure is visible. In code, print the surrounding characters:
try {
JSON.parse(text);
} catch (e) {
const pos = Number(/position (\d+)/.exec(e.message)?.[1] ?? 0);
console.log(JSON.stringify(text.slice(Math.max(0, pos - 40), pos + 40)));
}
Printing the slice with JSON.stringify makes invisible characters such as tabs and line breaks show up as escape sequences.
The most common causes of invalid JSON
Trailing commas
{ "id": 1, "tags": ["a", "b",], }
JavaScript and many other languages allow a comma after the last element; JSON does not. Remove it from both the array and the object.
Single quotes and unquoted keys
{ name: 'Ada' } // invalid
{ "name": "Ada" } // valid
JSON strings and keys must use double quotes. This usually happens when someone hand-writes JSON from a JavaScript object literal, or prints a Python dictionary with str() instead of json.dumps() — which also produces True, False and None instead of true, false and null.
Comments
// and /* */ comments are not part of JSON. Many editors accept them in files such as tsconfig.json because those tools use the JSONC dialect, but a standard parser will fail. Remove the comments, or use JSON5 where the consuming tool supports it.
Missing commas and brackets
A missing comma between two properties, or a ] where a } belongs, is common in hand-edited files and after merge conflicts. Formatting the document makes the broken nesting obvious because indentation stops matching the intended structure.
Bad escapes and control characters
Inside a string, a backslash must start a valid escape (\", \\, \/, \b, \f, \n, \r, \t or \uXXXX). A Windows path such as "C:\new\data" is therefore invalid — write "C:\\new\\data". Literal line breaks and tabs inside strings are also not allowed and must be written as \n and \t. When you need to embed arbitrary text, run it through JSON escape rather than escaping by hand.
Numbers JSON does not allow
NaN, Infinity, leading zeros (007), a leading plus sign, hexadecimal (0xFF) and a bare decimal point (.5) are all invalid. Some serialisers emit NaN by default — Python’s json.dumps does unless you pass allow_nan=False.
Copy-and-paste damage
Word processors and chat apps replace straight quotes with curly “smart” quotes, and some files start with an invisible byte-order mark (BOM). Both break strict parsers even though the text looks correct. Retyping the quotes or re-saving the file as UTF-8 without BOM fixes it.
The response is not JSON at all
Unexpected token < … at position 0: the server sent HTML — usually an error page, a proxy page or a login redirect.Unexpected end of JSON input: the body was empty or cut off. A204 No Contentresponse has no body to parse.- Several objects one after another: that is JSON Lines (one document per line), which must be parsed line by line.
- A quoted string that contains escaped JSON: the data was encoded twice and needs two parse calls — or better, fix the producer.
For API calls, check the status code and Content-Type before parsing, and log the raw text when parsing fails:
const res = await fetch(url);
const text = await res.text();
if (!res.ok || !res.headers.get('content-type')?.includes('application/json')) {
throw new Error(`HTTP ${res.status}: ${text.slice(0, 200)}`);
}
const data = JSON.parse(text);
Debugging JSON in browser developer tools
When the invalid JSON comes from an API, the browser’s network panel is the fastest place to look. Open the developer tools, reproduce the request and select it in the Network tab. The Headers section shows the status code and Content-Type; the Response section shows the raw body exactly as it arrived, before your code touched it. If the preview pane can render the body as a collapsible tree, the server sent valid JSON and the bug is in how your code reads it — for example calling JSON.parse on something that is already an object, which converts it to the string [object Object] and fails at position 1.
From the network panel you can also copy the request as a cURL command and replay it in a terminal. That separates server problems from client problems: if curl receives the same broken body, fix the API; if it receives valid JSON, look at proxies, service workers or client code that modifies the response.
Step 3: repair, then compare
For quick fixes to pasted data, the JSON fixer repairs common slips such as trailing commas, single quotes and unquoted keys. Always review the result: a tool can guess where a missing bracket belongs, but only you know the intended structure. If you changed a large document, compare the old and new versions with JSON diff to confirm only the syntax changed.
Valid JSON can still be wrong
Once the syntax parses, the next class of bugs is shape: a missing required field, an ID sent as a number instead of a string, or null where your code expects an array. Describe the expected structure as a JSON Schema and check payloads with the JSON Schema validator, both while debugging and in automated tests.
How to prevent invalid JSON
- Never build JSON with string concatenation or templates. Use your language’s serialiser —
JSON.stringify,json.dumps,json_encode— which handles quoting and escaping correctly. - Return errors from your API as JSON with the correct status code, so clients never receive an HTML page they try to parse.
- Validate configuration files in CI so a broken commit fails fast.
- If people need comments in configuration, choose a format designed for it rather than bending JSON.