Repair LLM JSON Output
AI responses often look like JSON but include markdown fences, comments, Python-style literals, or JavaScript object syntax.
Common LLM JSON Failure Modes
LLM responses look like JSON but routinely fail strict RFC 8259 parsing. The same dozen-or-so issues account for almost every broken response:
| What the model emits | Why JSON rejects it | Fix |
|---|---|---|
Markdown code fence (```json … ```) wrapping the JSON |
The fence and language tag are not JSON syntax | Strip the fence before parsing |
| Prose around the JSON ("Here's the JSON:" … "Hope this helps!") | Extra text after the closing brace | Extract the first balanced {…} or […] |
| Single quotes around strings or keys | JSON requires double quotes | See the fix-single-quotes guide |
Unquoted keys ({ name: "Ada" }) |
JSON requires keys to be quoted strings | See the fix-unquoted-keys guide |
| Trailing commas | RFC 8259 forbids them | See the fix-trailing-comma guide |
Python literals (True, False, None) |
JSON uses lowercase true / false / null |
Lowercase + replace None with null |
Line / block comments (//, /* */) |
JSON has no comments | Strip them |
Smart quotes ("…" curly-quoted) instead of straight " |
Different Unicode code points; the parser only accepts U+0022 | Replace with straight quotes |
| Truncated output (no closing brace) | The response cut off mid-document | Buffer until complete, or use a partial-JSON parser |
Broken LLM Response
A typical response from a chat model — looks like JSON, isn't:
Here's the JSON you asked for:
```json
{
// user record
'name': 'Ada Lovelace',
active: True,
notes: None,
tags: ['dev', 'lovelace',],
}
```
Hope this helps!
Eight separate things would have to go in the bin before JSON.parse accepts it: the leading prose, the fenced wrapper, a comment, single quotes around name, single-quoted string values, an unquoted active key, Python True / None, the trailing comma, and the trailing prose. Either the model has to be constrained at the source, or the response has to be repaired before parsing.
Repaired JSON
{
"name": "Ada Lovelace",
"active": true,
"notes": null,
"tags": ["dev", "lovelace"]
}
Same data, expressed as strict JSON.
Repair Workflow
- Strip the wrapper. Remove leading and trailing prose. If a markdown fence is present, extract the contents between
```jsonand the closing```. - Auto-repair the syntax. Paste the extracted text into JSON Fix — it handles fences, single quotes, unquoted keys, trailing commas, Python
True/False/None, and line / block comments in one pass, in your browser. In Node, thejsonrepairnpm package does the same programmatically with zero dependencies. - Validate strictly. Run the result through the strict JSON validator (or
JSON.parse) to confirm RFC 8259 conformance before using the data. - Validate the shape. Auto-repair fixes the grammar, not the schema. Run the parsed object through a schema check (Zod, Ajv, Pydantic, or a hand-rolled validator) to confirm required fields and types are present before acting on it.
Tools and Recipes
The browser tool (JSON Fix) and the jsonrepair npm package implement the same repair rules. For partial / streaming responses, see partial-json on npm — it returns the best valid prefix of an in-progress JSON document so a UI can render fields as they arrive.
// Node — repair, then parse, then validate the shape
import { jsonrepair } from 'jsonrepair';
import { z } from 'zod';
const Schema = z.object({ name: z.string(), active: z.boolean() });
const raw = await callTheModel();
const repaired = jsonrepair(raw); // fixes grammar
const parsed = JSON.parse(repaired); // confirms strict JSON
const value = Schema.parse(parsed); // confirms the shape
Prompting to Reduce Repair Work
The cheapest fix is the one you never need. Constrain the output:
Respond with a single JSON object and nothing else.
No markdown fence, no commentary, no leading or trailing text.
If the model provider supports it, use structured outputs at the API level — they eliminate fences and prose by construction, not by hope:
- OpenAI: pass
response_format: { type: "json_object" }(free-form JSON) orresponse_format: { type: "json_schema", json_schema: { … } }(schema-conformant) - Anthropic: use tool use / function calling — the input_schema constrains the model's output to a parseable shape
- Google Gemini: pass
response_mime_type: "application/json"with an optionalresponse_schema
Function-calling-style schemas (the most constraining option across providers) further reduce hallucinated keys.
Streaming JSON from an LLM
While tokens are streaming, the partial response is invalid by definition — there's no closing brace yet. Two patterns work:
- Buffer then parse. Accumulate the full response, then
JSON.parseonce. Simplest and most reliable. Use this when the response is small or the user can wait. - Streaming-tolerant parser. Use
partial-json,jsonrepair, orclarinetto return the best valid prefix on each chunk, so a UI can render fields as they arrive. Required for token-by-token rendering of long structured outputs.
Either way, the final parse should be strict — streaming-tolerant parsing is for the in-progress display, not for the value you act on.
When to Fail Loudly Instead of Repairing
If the JSON drives a financial transaction, a permission change, or any destructive action, do not silently repair — reject the response and retry, or surface the raw output for a human to review. Repair is appropriate for display, debugging, and recovery; never for inputs that change state without human review. The same logic applies to LLM agents calling tools: an agent that auto-repairs malformed tool-call JSON can take the wrong action on a quietly-corrupted payload.
A reasonable default: repair-and-log in dev, repair-and-alert in staging, strict-parse-or-refuse in production for any action with side effects.
Privacy Note
Repair happens locally in the browser when you use JSON Fix — useful when debugging prompts that include internal examples, real API payloads, or anything you can't paste into a hosted tool. Same applies to jsonrepair running in your Node process: nothing leaves the box.
See also
This guide is part of the LLM-output repair workflow — strip code fences, fix quote style, validate before parsing.
Sources
- RFC 8259 — the JSON Data Interchange Format (IETF, the canonical JSON grammar)
jsonrepair— repair invalid JSON programmatically in Node or the browserpartial-json— streaming-tolerant JSON parser for in-progress LLM output- OpenAI — Structured Outputs (
response_format) - Anthropic — Tool use (schema-constrained output)
- Google — Gemini structured output (
response_mime_type)
Last reviewed June 2026.
JSON repair guides
Topic hubs
- JSON Parse Errors: Read the Message, Jump to the Fix
- Fix Invalid JSON: From 'What's Wrong' to a Clean File
- JSON Formatter, Validator, Viewer: Pick the Right Tool
- Repair LLM JSON Output: Handling Almost-JSON from AI
- Privacy: JSON Tools That Don't Leave Your Browser
- JSON Interop: YAML, CSV, XML, JWT, Schema
Specific guides
- How to Decode Base64 Strings (and JWT Payloads)
- URL Encoding: Percent-Encode Query Parameters and Paths
- Convert YAML to JSON (and Avoid Indentation Errors)
- Convert JSON to CSV: Flatten an Array of Objects
- Convert JSON to XML: Root Elements, Attributes, and Arrays
- Escape JSON as a String Literal (and Decode Double-Encoded JSON)
- Fix Trailing Comma in JSON
- Fix Single Quotes in JSON
- Fix Unquoted Keys in JSON
- Fix JSON Parse Error: Expected Property Name
- JSON vs JS Object Literal: The Key Differences
- Validate JSON Before API Requests
- JSON Formatter vs JSON Repair
- Fix JSON Unexpected Token Errors
- JSON to JavaScript Object Converter