JSON Repair — Fix Broken JSON (incl. ChatGPT/LLM Output)
100% in your browser
Your data is never uploaded. All processing happens locally on your device.
Read privacy policy →Last updated: 2026-09-17
Paste broken or malformed JSON — including output from ChatGPT, Claude, and other LLMs — and this tool repairs it automatically. It strips markdown fences and prose, fixes trailing commas, single quotes, unquoted keys, Python literals, NaN/Infinity, and closes truncated output. You get the repaired JSON plus a list of every fix applied. Everything runs locally in your browser — no data uploaded.
What this tool does
Repairs malformed JSON by applying a series of fixes in sequence: stripping markdown fences and prose, unwrapping escaped strings, removing comments, converting single quotes and Python literals, removing trailing commas, replacing NaN/Infinity with null, and closing truncated brackets. The jsonrepair library handles structural fixes like missing commas and unquoted keys. The tool reports every fix applied in a list so you can verify the result.
Example
Input (ChatGPT output with prose and fences):
Sure! Here's the JSON:
```json
{
"users": [
{"id": 1, "name": "Alice"},
{"id": 2, "name": "Bob"}
]
}Let me know if you need changes!
Output:
```json
{
"users": [
{ "id": 1, "name": "Alice" },
{ "id": 2, "name": "Bob" }
]
}Fixes reported:
- Removed leading prose before JSON
- Removed markdown code fence
- Removed trailing prose after JSON
What gets fixed
| Fix | Example input | Example output |
|---|---|---|
| Markdown fences | ```json {...} ``` | {...} |
| Leading/trailing prose | Here's the JSON: {...} | {...} |
| Single quotes | {'a':1} | {"a":1} |
| Unquoted keys | {name:"test"} | {"name":"test"} |
| Trailing commas | {"a":1,} | {"a":1} |
| Python booleans | {"active":True} | {"active":true} |
| Python None | {"value":None} | {"value":null} |
| NaN/Infinity | {"score":NaN} | {"score":null} |
| Truncated output | {"items":["a","b","c" | {"items":["a","b","c"]} |
| Escaped string | "{\"a\":1}" | {"a":1} |
<json> tags | <json>{"a":1}</json> | {"a":1} |
Repairing ChatGPT output
ChatGPT and similar chat models rarely return raw JSON. They introduce themselves, wrap the payload in a markdown fence, and add a closing remark. Each of those decorations breaks a strict parser. The example below is a realistic assistant response containing a fenced object with a trailing comma, Python-style capitalization on a boolean, and prose on both ends.
Input:
Certainly, here is the configuration you asked for:
```json
{
"service": "billing",
"timeout_ms": 30000,
"retries": 3,
"secure": True,
"endpoints": [
"https://api.example.com/charge",
"https://api.example.com/refund",
]
}Let me know if you want me to adjust the timeout or retries.
Output:
```json
{
"service": "billing",
"timeout_ms": 30000,
"retries": 3,
"secure": true,
"endpoints": [
"https://api.example.com/charge",
"https://api.example.com/refund"
]
}Fixes reported:
- Removed leading prose before JSON
- Removed markdown code fence
- Removed trailing prose after JSON
- Converted Python boolean True to true
- Removed trailing comma inside the endpoints array
The two prose lines and the fence would each cause a parser to fail before it ever reached the object. Stripping them first lets the rest of the pipeline operate on the payload itself.
Repairing truncated API responses
When a streaming or token-limited response cuts off mid-value, the JSON ends without the closing brackets a parser needs. The tool detects the unclosed structures and appends the missing delimiters so the result parses. It also reports how many brackets were added, which tells you the output is reconstructed rather than verbatim.
Input (response cut off mid-array):
{
"query": "laptop stands",
"total": 214,
"results": [
{"id": "A1", "title": "Adjustable Aluminum Stand", "price": 39.99},
{"id": "A2", "title": "Foldable Bamboo Stand", "price": 24.50},
{"id": "A3", "title": "Portable Tablet StanOutput:
{
"query": "laptop stands",
"total": 214,
"results": [
{ "id": "A1", "title": "Adjustable Aluminum Stand", "price": 39.99 },
{ "id": "A2", "title": "Foldable Bamboo Stand", "price": 24.5 },
{ "id": "A3", "title": "Portable Tablet Stan" }
]
}Fixes reported:
- Closed 1 unclosed string
- Closed 1 unclosed object
- Closed 1 unclosed array
- Closed 1 unclosed root object
The last title was truncated mid-word, so the tool closes the string with what it has. The data is incomplete by nature of truncation, but the structure is now valid and you can inspect every returned field. If you control the request, increase the token budget or request a smaller page to avoid the cut-off at the source.
Repairing Python-style JSON
Python developers often paste output from print() or a REPL session straight into a JSON tool. Python dict literals look like JSON but use single quotes, capitalize booleans, and write None instead of null. They also tolerate trailing commas, which JSON does not. The tool normalizes all of these.
Input:
{'project': 'orion', 'version': '1.4.2', 'enabled': True, 'archived': False, 'owner': None, 'tags': ['internal', 'staging',], 'config': {'port': 8080, 'host': '0.0.0.0'}}Output:
{
"project": "orion",
"version": "1.4.2",
"enabled": true,
"archived": false,
"owner": null,
"tags": [
"internal",
"staging"
],
"config": {
"port": 8080,
"host": "0.0.0.0"
}
}Fixes reported:
- Converted single quotes to double quotes
- Converted Python boolean True to true
- Converted Python boolean False to false
- Converted Python None to null
- Removed trailing comma inside the tags array
Single-quote conversion is a text-level pass, not a full Python parser, so it assumes the input is dict-like. If your Python source uses f-strings that embed quotes in values, verify the output by eye before trusting it.
The repair pipeline explained
The tool runs the input through an ordered pipeline. Each step hands its result to the next. Reading them in order explains why a particular fix appears (or does not) in the report.
- stripMarkdownFences removes triple-backtick fences and any
jsonlanguage tag so the parser is not tripped up by```markers. - stripXmlTags removes wrapper tags such as
<json>and</json>that some agents insert around the payload. - unwrapEscapedJson detects when the whole value is a JSON string containing escaped JSON (for example a stringified webhook body) and unwraps it into the inner object.
- stripLeadingProse drops conversational text before the first JSON token, such as “Sure, here is the data:”.
- stripTrailingProse drops conversational text after the last JSON token, such as “Let me know if you need changes!”.
- extractJsonBlocks finds each JSON block in what remains and selects the first one. The count is reported so you can choose a different block if the first was not the one you wanted.
- Replace NaN and Infinity with null converts values that are not legal in JSON into null. The report notes how many were replaced.
- Fix trailing commas, single quotes, and Python literals removes trailing commas, swaps single quotes for double quotes, and rewrites True, False, and None into true, false, and null.
- jsonrepair runs next and handles structural problems the text passes cannot: missing commas between properties, unquoted keys, unterminated strings that need more than a simple close, and mismatched brackets that require insertion rather than just appending.
- detectStructuralFixes compares the repaired structure against the original to determine which closings and insertions were structural, so the report can describe them in plain language (for example “Closed 2 unclosed arrays”).
- Pretty-print runs JSON.stringify with two-space indentation on the final value so the output is readable and deterministic.
The order matters. Stripping prose and fences first means the later passes never see the conversational wrapper. Replacing NaN before jsonrepair prevents the library from trying to interpret invalid numeric literals. Running jsonrepair after the text passes means it only has to handle residual structural damage, which is exactly what it is built for.
Common mistakes when repairing
- Pasting YAML and expecting JSON output. YAML uses indentation and unquoted strings; this tool will not convert it. Convert YAML with a dedicated parser first.
- Treating the fixed output as a guarantee of correctness. Repair makes the JSON parse, it does not verify the values are the ones you wanted. Truncated strings in particular are closed with whatever characters survived.
- Ignoring the fixes list when debugging. If “Closed 3 unclosed arrays” appears, the input was truncated and the array contents are partial. Do not ship the result without checking the tail.
- Assuming key order reflects the original. The output comes from JSON.stringify, which preserves insertion order of parsed objects but not the byte order of the raw text. If order matters, compare against the source.
- Forgetting that NaN becomes null, not zero. Numeric code that expects a number will now see null and may throw downstream. Update consumers or use the schema validator to flag it.
- Trusting single-quote conversion on string values that contain apostrophes. The text pass swaps every single quote, including ones inside words. Review values like “it’s” before relying on the output.
- Running repair on input that already contains nested escaped JSON and expecting the inner string to stay escaped. unwrapEscapedJson flattens one level of string wrapping by design.
When repair cannot help
Some inputs are damaged past the point of structural reconstruction. The tool will either report that it could not parse the input or produce a best-effort result that is syntactically valid but semantically wrong. Watch for these cases:
- The input is not JSON-like at all. CSV rows, YAML, XML, log lines, and free-form prose without an object or array cannot be turned into JSON by this pipeline.
- Binary or encoded payloads. Base64, gzip, protobuf, or msgpack bytes are not text JSON and will not survive the text passes.
- Truncation that cut inside a number or keyword. If a response ends with
"price": 39.the tool may close the value as a string or drop it, neither of which recovers the intended number. Re-request the data instead. - Two JSON objects concatenated with nothing between them. extractJsonBlocks takes the first block; the second is ignored. If you need both, split the input yourself.
- Heavily redacted or masked values. Fields replaced with asterisks or ellipses may parse but will not round-trip to the original type the producer intended.
- Encrypted or tokenized payloads. If the body is a JWE, signed token, or encrypted blob that happens to look like JSON, repair will not decrypt it and may mangle the structure.
- Inputs where the producer used a non-standard extension. Duplicate keys, functions, or comments in positions the passes do not target can still confuse the result. Check the fixes list and the output together.
In all of these cases the right move is to fix the source: re-prompt the model, re-issue the API call with a higher token budget, or parse the original format with the correct tool.
Do it in code
Node.js (jsonrepair):
const { jsonrepair } = require('jsonrepair');
try {
const repaired = jsonrepair(brokenJson);
const parsed = JSON.parse(repaired);
console.log(parsed);
} catch (e) {
console.log('Could not repair:', e.message);
}Python:
import json, re
def repair_json(s):
# Strip markdown fences
s = re.sub(r'^```(?:json)?\s*', '', s, flags=re.MULTILINE)
s = re.sub(r'\s*```$', '', s, flags=re.MULTILINE)
# Strip prose before/after
s = s[s.find('{'):s.rfind('}')+1] if '{' in s else s[s.find('['):s.rfind(']')+1]
# Fix single quotes, Python bools, trailing commas
s = s.replace("'", '"').replace('True', 'true').replace('False', 'false').replace('None', 'null')
s = re.sub(r',(\s*[}\]])', r'\1', s)
return json.loads(s)CLI (jsonrepair):
npx jsonrepair broken.json > fixed.jsonThe code snippets above cover the common cases but do not replicate the full browser pipeline. The hosted tool adds prose stripping, escaped-string unwrapping, NaN replacement, truncation closing, and a human-readable fixes report on top of the library.
Edge cases handled
- Markdown code fences with or without language tags (
json,JSON, or none) are stripped. - Leading prose like “Sure! Here’s the JSON:” is removed before parsing.
- Trailing prose like “Let me know if you need changes!” is removed after the last JSON closing bracket.
- Multiple JSON blocks in the input: the first one is used and the count is reported.
- Escaped-string JSON (a JSON string value that contains JSON) is unwrapped automatically.
- Truncated output from token-limited LLMs is closed by adding the missing brackets.
- NaN and Infinity (not valid in JSON) are replaced with null.
- The repaired output is pretty-printed with 2-space indentation for readability.