---
title: "Problems parsing JSON"? Find the error with the JSON Formatter & JSONPath
description: An API answers 400 "Problems parsing JSON" and doesn't say where. Paste the body into a JSON validator and it names the line and column where parsing stopped; the mistake is almost always the character just before it. Here's a real case from the GitHub API, how to read the error, how to format and query a response with JSONPath, and the jq one-liners that do the same.
canonical: https://watchfor.io/blog/how-to-format-and-query-json
---

# "Problems parsing JSON"? Find the error with the JSON Formatter & JSONPath

2026-10-04 · WatchFor Team · Guides · guides, api

An API answers 400 "Problems parsing JSON" and doesn't say where. Paste the body into a JSON validator and it names the line and column where parsing stopped; the mistake is almost always the character just before it. Here's a real case from the GitHub API, how to read the error, how to format and query a response with JSONPath, and the jq one-liners that do the same.

To find a JSON syntax error, run the text through a strict parser and look
at the position it reports: that's where the parser gave up, and the actual
mistake (a trailing comma, a single quote, a missing quote or bracket) is
usually on that spot or just before it. Fix it, re-validate, and only then
look at the server, because a body that doesn't parse never reached your
code's logic.

The free [JSON Formatter & JSONPath](/json-formatter) tool validates,
formats and minifies JSON, gives the line and column of the first error, and
lets you query the document with JSONPath. It runs in your browser, so the
payload never leaves the page.

## What this tool answers

*Is this valid JSON, and if not, where is it broken?* And once it's valid:
*what does this response contain, and which path gets me the value I need?*
Reach for it when an API rejects your request body, when a config file
won't load, when a response arrives as one unreadable line, or when you're
writing a JSONPath for a test, a script or a monitor and want to see what it
matches before relying on it.

## A real example

### Validating JSON when an API says "Problems parsing JSON"

We sent a request body to GitHub's Markdown API
(`POST https://api.github.com/markdown`, which needs no token), typed by hand
the way you would in a `curl -d` or a Postman tab:

```json
{
  "text": "Release **v2.14.1** is live. Fixes the login timeout.",
  "mode": "markdown",
}
```

GitHub answered `HTTP/2 400` with
`{"message": "Problems parsing JSON", "status": "400"}`. True, but no line
number. We pasted the same 92 bytes into the
[JSON Formatter & JSONPath](/json-formatter):

![The JSON Formatter with the hand-written GitHub request body pasted in: Format and Minify are greyed out and a red line under the editor reads "Expected double-quoted property name in JSON at position 91 — line 4, column 1"](/blog/how-to-format-and-query-json-error.webp)

The red line says **"Expected double-quoted property name in JSON at
position 91, line 4, column 1"**, and **Format** and **Minify** are disabled
because there's nothing valid to reformat. Line 4, column 1 is the closing
brace. The parser read the comma after `"markdown"`, so it expected another
`"key": value` pair; it found `}` instead and stopped there. The mistake is
the comma on line 3, one character earlier than the reported position.

We deleted that comma, the tool turned green (**Valid JSON**), and the same
request to GitHub returned `200` with
`<p>Release <strong>v2.14.1</strong> is live. Fixes the login timeout.</p>`.

**The answer:** one trailing comma, which strict JSON (RFC 8259) doesn't
allow, even though JavaScript object literals do.

Then the second half of the job: reading a response. GitHub's
`/rate_limit` endpoint returns how many requests are left in each bucket.
We pasted it minified (one 423-byte line), pressed **Format** and asked for
`$.resources.*.remaining`:

![The formatted GitHub rate_limit response in the editor, marked Valid JSON, 677 bytes (423 minified), and below it the JSONPath query $.resources.*.remaining with 5 matches: 58, 58, 0, 5000 and 10](/blog/how-to-format-and-query-json-query.webp)

Five matches, in document order: `code_search` 58, `core` 58, `graphql` 0,
`integration_manifest` 5000 and `search` 10. So this IP had used 2 of its 60
unauthenticated core requests. The `reset` field next to each value is a
Unix timestamp; [how to convert a Unix timestamp](/blog/how-to-convert-a-unix-timestamp)
shows what time that is.

## How to read the results

| What you see | What it means |
| --- | --- |
| **Valid JSON · 677 bytes (423 minified)** | The text parses. The first number is its size as typed (UTF-8 bytes), the second the size with all whitespace removed. |
| **Red error with position, line and column** | The first place the parser couldn't continue. Position is a 0-based character offset; line and column are counted from 1. The wording comes from your browser's JavaScript engine, so Firefox and Chrome phrase it differently. |
| **Format / Minify greyed out** | The document is invalid; fix the error first. |
| **Colours in the editor** | Keys blue, strings green, numbers amber, `true`/`false`/`null` violet. Colouring is skipped above 100,000 characters to keep typing fast. |
| **N matches** | How many values the JSONPath selected. Each is shown as JSON with a copy button. Only values are listed, not the path each came from, so with a wildcard read them in document order. |
| **0 matches** | The path is valid but selects nothing: wrong key, missing `$`, or an array without an index or `[*]`. |
| **Showing the first 50** | Long result lists are cut at 50 rows; the count above is still the full number. |

Supported JSONPath: `$`, `.key` and `['key with spaces']`, indexes
including negative (`[-1]` is the last element), slices (`[1:3]`),
wildcards (`.*`, `[*]`) and recursive descent (`$..remaining`). Filter
expressions like `[?(@.used > 0)]` are not supported.

## More ways to use it

- **A config file won't load.** `package.json`, a Terraform variables file
  or a `.json` fixture throwing `Unexpected token` gets the same line and
  column treatment.
- **A webhook payload in a log.** Paste the one-line body, **Format** it and
  read it like a document instead of scrolling sideways.
- **Writing an API test assertion.** Try the path against a real response
  first: `$.data.items[0].id` matching nothing tells you the shape is
  different from what the docs promised.
- **Checking size before you ship it.** The minified size is what goes over
  the wire without compression; a 2 MB response that is 1.4 MB minified is
  mostly indentation.

## Tips and gotchas

- **Look one step back from the reported position.** A parser reports
  where it noticed the problem, not where you made it. Trailing commas show
  up on the next line's `}` or `]`; an unclosed string shows up at the end of
  the document.
- **Strict JSON allows none of these:** trailing commas, comments,
  single-quoted strings, unquoted keys, `NaN`. They come from JSON5, JSONC
  (VS Code settings) or JavaScript. If a file needs comments, the program
  reading it has to support JSONC.
- **Format and Minify rewrite through JavaScript.** Numbers beyond 2^53 lose
  precision (`12345678901234567890` comes back as `12345678901234567000`),
  duplicate keys collapse to the last one, `1.0` becomes `1` and `é`
  becomes `é`. Validate large-ID payloads, but don't copy the reformatted
  version back if exact digits matter.
- **Smart quotes break everything.** Text pasted from a chat app or a
  document often carries `“ ”` instead of `"`. The error lands on the first
  one.
- **JSONPath dialects differ.** WatchFor
  [API monitor](/docs/monitors/api) assertions use dot-style paths: a leading
  `$.` is accepted and plain keys carry over (`$.resources.core.remaining`
  works as is), but array indexes are written `items.0.id` and "every
  element" is `items.#.id`. Paths with `[0]`, `[*]` or `..` from this tool
  need rewriting before you paste them into a monitor.

## Check it from the command line too

`jq` validates and queries in one step:

```bash
jq . body.json
```

```text
jq: parse error: Expected another key-value pair at line 4, column 1
```

Same spot as the browser. Python's built-in validator is more direct about
the cause:

```bash
python3 -m json.tool body.json
```

```text
Illegal trailing comma before end of object: line 3 column 21 (char 89)
```

The JSONPath query from the example, in jq syntax, with the key names kept:

```bash
curl -s https://api.github.com/rate_limit -o rate_limit.json
jq '.resources | map_values(.remaining)' rate_limit.json
```

```text
{
  "code_search": 58,
  "core": 58,
  "graphql": 0,
  "integration_manifest": 5000,
  "search": 10
}
```

`jq -c .` minifies, `jq .` formats, and `jq -e` sets a non-zero exit code
when the result is `null` or `false`, which makes it usable in CI scripts.

## Keep watching it

A response that parses today can change shape after the next deploy. A
[WatchFor API monitor](/api-monitoring) calls the endpoint on a schedule
from several locations, asserts on JSON fields (equals, greater than,
exists, matches regex and more), and opens an incident when an assertion
fails, showing the expected and actual value. Numeric fields such as
`resources.core.remaining` can also be extracted and charted over time.
[API monitoring beyond status codes](/blog/api-monitoring-beyond-status-codes)
covers which fields are worth asserting on.

## FAQ

**How do I find the line with a JSON syntax error?**
Paste it into a validator that reports position, such as the
[JSON Formatter](/json-formatter), `jq .` or `python3 -m json.tool`. Then
check that line and the end of the line before it; trailing commas and
missing quotes are the usual causes.

**Why is my JSON invalid when it looks fine?**
The usual invisible causes are a trailing comma after the last item, single
quotes, curly "smart" quotes from a word processor, a comment, or an
unquoted key. All of them are legal in JavaScript and illegal in JSON.

**What is the difference between JSONPath and jq?**
JSONPath selects values (`$.resources.core.remaining`); jq is a full
language that also filters, reshapes and computes. For "give me this field",
either works. To build a new document out of the old one, use jq.

---

Canonical page: https://watchfor.io/blog/how-to-format-and-query-json · All articles: https://watchfor.io/blog/all.md · Site guide: https://watchfor.io/llms.txt
