json

FormatValidateConvert

In this section · 03 Fix itRepair

03 · Fix it

Repair broken JSON

One press turns a JavaScript object, a Python dictionary, an answer from a model or a pile of log lines into JSON that parses — and refuses when guessing would be worse.

Guide 3 of 9 in Editor

Most text that a parser rejects is not really broken. It is an object lifted out of a JavaScript source file, a dictionary printed by a Python shell, an answer from a language model with a sentence wrapped round it, or a log file with one record per line. All of it is recognisable; none of it is JSON. Repair in the editor takes that text and makes it parse, naming every change it made, and refuses when the honest answer is that it cannot tell what you meant.

The button sits in the editor band under On the left pane, and again in each pane’s own toolbar, where it turns amber while that document does not parse. Press it and the status bar answers with Repaired followed by the list of fixes, each one a sentence, joined with a middle dot. The examples below print that same list one sentence to a line so a long one does not run off the side of the block; the wording is identical either way, and it is produced by running the real engine over the input you see.

Nothing is applied blindly. Whatever the repair produces is parsed again before it is allowed to replace your text, and if the result still does not parse the document is left exactly as it was. The repair lands as one change, so a single ⌘Z on a Mac, or Ctrl+Z on Windows and Linux, puts the original back. That is worth knowing before you read on: pressing Repair to see what happens costs nothing.

Fix invalid JSON: comments, quotes and commas

The longest worked example is a staging config copied straight out of a .js file, with comments, unquoted keys, single quotes, an undefined and two trailing commas — five different problems in eleven lines, which is what real pasted config looks like. That guide works it through from the paste to the status line, then covers what the repair deliberately leaves alone, and why your indentation and key order survive it.

JSON from an AI model

Input

Sure! Here's the config you asked for:

```json
{
  "service": "orders-api",
  "retries": 3
}
```

Let me know if you want it as YAML.

Do

  1. Paste the input into the left pane of the editor.
  2. Press Repair in the editor band.

Result

Removed a Markdown code fence
Removed text around the code block

{
  "service": "orders-api",
  "retries": 3
}

Try it in the editor →

A model asked for JSON tends to answer like a person: a line of introduction, a Markdown code fence marked json, and an offer to do something else afterwards. Repair recognises the fence, drops the chat around it and keeps what was inside, and it says both things separately — the fence, then the text around the block — so you can see that nothing from inside the fence was touched.

It also copes with the answer that ran out of room. A reply that stops at {"items": [1, 2, 3 comes back as a complete object with its array and its brace closed, reported as Closed unterminated brackets. What it cannot do is invent the items that never arrived, so check the tail of anything it had to finish for you.

Two fenced JSON blocks in one answer are refused rather than guessed between, with the count in the message: This has 2 JSON code blocks — delete all but the one you want, then Repair. A block marked as another language is refused by name — That code block is marked yaml, not JSON. — and for that one the Convert page is the right tool.

Values from another language

Input

{
  'service': 'orders-api',
  'active': True,
  'deleted': False,
  'owner': None,
  'retries': undefined,
  'ratio': NaN
}

Do

  1. Paste the input into the left pane of the editor.
  2. Press Repair in the editor band.

Result

Converted single-quoted strings (7)
Converted Python literals (3)
Replaced NaN and Infinity with null
Replaced undefined with null

{
  "service": "orders-api",
  "active": true,
  "deleted": false,
  "owner": null,
  "retries": null,
  "ratio": null
}

Try it in the editor →

A dictionary printed by Python and an object pasted out of JavaScript bring their own spellings for things JSON writes differently. True, False and None become true, false and null, counted together as Python literals. Single quotes become double ones, keys and values alike, which is why the count here is seven rather than six.

undefined and NaN are the two that cannot be translated, only replaced. JSON has no word for an absent value beyond null, and its number grammar has no room for a not-a-number or an infinity, so both are written as null and each is reported on its own line. Nothing is invented in their place: inventing a number would be the one change you could not spot later. If a field mattering to you comes back null, that line of the list is where to look.

Log lines and NDJSON

Input

{"level": "info", "msg": "started", "ms": 12}
{"level": "warn", "msg": "slow query", "ms": 940}
{"level": "error", "msg": "timeout", "ms": 3000}

Do

  1. Paste the input into the left pane of the editor.
  2. Press Repair in the editor band.

Result

Wrapped multiple top-level values in an array

[{"level": "info", "msg": "started", "ms": 12},
{"level": "warn", "msg": "slow query", "ms": 940},
{"level": "error", "msg": "timeout", "ms": 3000}]

Try it in the editor →

A log file, a JSON Lines export and the output of a streaming API all look the same: one complete object per line, no commas between them and no brackets around the lot. Each line is valid on its own, which is what makes the format useful, and the whole file is not a JSON document at all — a document has exactly one top-level value.

Repair wraps the run of values in an array and says so, and the result is something the rest of the editor can work with: the tree shows one row per record, the table gives you a column per field, and a search or a JSONPath query runs over the lot. The line breaks are kept where they were, so a thousand-line log stays a thousand readable lines rather than becoming one very long one. Press Format afterwards if you would rather it were indented.

When Repair refuses

Input

The upstream service replied with 502 and the body below.
Retry after 30 seconds.
{ "error": "bad gateway", "retryAfter": 30 }
Nothing else was logged.

Do

  1. Paste the input into the left pane of the editor.
  2. Press Repair in the editor band.

Result

Nothing here could be repaired automatically.

Try it in the editor →

There is an object in that text, and Repair leaves it alone. The status bar says Nothing here could be repaired automatically. and the document is untouched.

This is the deliberate limit, not a gap. Pulling a {…} out of arbitrary prose means guessing where the document starts and stops, and the thing that most often arrives wrapped in prose is an HTML error page with a fragment of JSON somewhere inside it. Guessing there would hand you a document that parses and is not the one the server meant, which is worse than a refusal because you would have no reason to check it. A code fence is different: somebody drew that boundary on purpose, so the fenced case above is repaired and this one is not.

A separate message covers the other kind of failure. When the fixes do apply but the result still will not parse, the document is kept and the bar reads Could not repair this automatically — the remaining errors are marked in the editor. The underlines are then the place to look, and fixing the first by hand usually unblocks the rest.

Format, Minify and Sort keys

Input

{ "name": "orders-api",
    "retries": 3,
  "active": true, "tags": ["eu","prod"] }

Do

  1. Paste the input into the left pane of the editor.
  2. In the pane toolbar, under Transform, press Format; undo, press Minify; undo, press Sort keys.
  3. Open Settings, set Indent to 4, undo once more and press Format again.

Result

Format
{
  "name": "orders-api",
  "retries": 3,
  "active": true,
  "tags": [
    "eu",
    "prod"
  ]
}

Minify
{"name":"orders-api","retries":3,"active":true,"tags":["eu","prod"]}

Sort keys
{
  "active": true,
  "name": "orders-api",
  "retries": 3,
  "tags": [
    "eu",
    "prod"
  ]
}

Format, with Indent set to 4
{
    "name": "orders-api",
    "retries": 3,
    "active": true,
    "tags": [
        "eu",
        "prod"
    ]
}

Try it in the editor →

Once a document parses, three more commands rewrite it without changing what it means. They sit in each pane’s toolbar under Transform. Format prints it with one value a line and the nesting indented; Minify removes every space and line break, which is what you want before pasting into an environment variable; Sort keys puts every object’s keys in alphabetical order while leaving array order alone, because an array’s order is data and an object’s is not.

Format and Sort keys both write the indent set in Settings, which offers two, three or four spaces or a tab, and they read it at the moment you press — so the fourth run above differs from the first only in that setting. Sorting the keys of two files before comparing them is the quickest way to stop a diff reporting a reordering as a change.

Before you press it

Two habits make the button more useful than it first looks. The first is to paste the whole thing rather than the part you think is the JSON. Repair is better at finding the boundary than a person scrolling is, and the cases it handles — a fence, a prefix, a run of records — are all about working out where the document begins and ends. Trimming first throws away the evidence it would have used.

The second is to read the list afterwards rather than the document. A repaired file looks right, because looking right is what the repair did; the list is the only place that says what was changed to get there. It takes a second to scan, and it is where a surprise shows up — a count larger than the number of fields you expected, or a value replaced rather than translated.

If the document parses already, the button does nothing and says so. Nothing here reformats a valid file as a side effect: that is what the three transforms below are for, and keeping them separate is what lets you repair a config without the diff filling up with re-indentation.

What Repair will not change

A repair rewrites only the characters that were wrong. Indentation, line breaks and key order survive it, so the result still reads as the file you pasted and a review of it shows the real fix rather than a reformatting of everything. That is the property that makes it usable on a two-thousand-line config, and it is why Format is a separate command rather than part of the repair.

Numbers are preserved exactly as they were written, digit for digit, including ones too large for a JavaScript number to hold. A repair never rounds, never re-spells an exponent it did not have to touch, and never drops a fractional zero. Strings keep their escapes unless an escape was itself invalid.

And it changes nothing anywhere else. Repair runs on the pane you pressed it in, on your own machine, with no request made to any server — so a config full of hostnames, keys and customer identifiers is safe to paste here in a way it would not be on a site that repairs JSON by uploading it.

Open the editor and paste whatever will not parse, or read the worked example in Fix invalid JSON: comments, quotes and commas.