DEV Community

Aaron Decker
Aaron Decker

Posted on

Stop JSON.parse From Crashing on LLM Responses

I want to show you every way an LLM can break your JSON.parse call, and how to handle all of them in one line.

The many ways LLMs break JSON

If you're calling OpenAI, Claude, Gemini, Ollama, or any other LLM and asking for JSON, here's what you'll eventually get back:

1. Markdown code fences

The most common one. You ask for JSON, the model gives you a helpful little markdown block:

Sure! Here you go:

​```
{% endraw %}
json
{"score": 95}

{% raw %}
Enter fullscreen mode Exit fullscreen mode



**2. Trailing commas**

Models love trailing commas. Especially in arrays.



```json
{"items": ["a", "b", "c",], "count": 3,}
Enter fullscreen mode Exit fullscreen mode

3. Unquoted keys

The model writes JavaScript instead of JSON:

{name: "Alice", age: 30, active: true}
Enter fullscreen mode Exit fullscreen mode

4. Single quotes

{'name': 'Alice', 'city': 'New York'}
Enter fullscreen mode Exit fullscreen mode

5. Smart quotes and em dashes

Copy-paste artifacts or just models being fancy:

{"name": "Alice", "range": "10—20"}
Enter fullscreen mode Exit fullscreen mode

Those " and characters look right but they are not ASCII and JSON.parse will reject them.

6. Inline comments

{
  "name": "Alice", // the user
  "age": 30 /* years */
}
Enter fullscreen mode Exit fullscreen mode

7. JSON buried in a paragraph

Based on the analysis, the result is {"score": 87, "pass": true} for this input.
Enter fullscreen mode Exit fullscreen mode

8. Invisible unicode

UTF-8 BOM (\uFEFF) at the start, zero-width spaces (\u200B) between characters. You can't see them. Your parser can.

The one-line fix

npm install ai-json-safe-parse
Enter fullscreen mode Exit fullscreen mode
import { aiJsonParse } from 'ai-json-safe-parse'

const result = aiJsonParse<{ score: number }>(llmResponse)

if (result.success) {
  console.log(result.data.score)
} else {
  console.log(result.error)
}
Enter fullscreen mode Exit fullscreen mode

That handles every case above. It runs a recovery pipeline — tries direct parse first, then strips markdown, normalizes unicode, does bracket matching to extract JSON from prose, and if needed fixes trailing commas, quotes, keys, and comments.

It returns a typed discriminated union. Never throws. If you want simpler APIs:

// Returns T | null
const data = aiJsonSafeParse<MyType>(llmResponse)

// Returns T or your fallback
const data = aiJsonSafeParse(llmResponse, { score: 0 })

// Throws on failure
const data = aiJsonStrictParse<MyType>(llmResponse)
Enter fullscreen mode Exit fullscreen mode

Safe vs aggressive

By default it uses aggressive mode — it will actually repair broken syntax. If you only want extraction (markdown stripping, bracket matching) without modifying the JSON:

const result = aiJsonParse(text, { mode: 'safe' })
Enter fullscreen mode Exit fullscreen mode

Details

  • Zero dependencies
  • ~2KB gzipped
  • TypeScript with full generics
  • ESM and CommonJS
  • Works in Node.js, browsers, Cloudflare Workers, Deno

GitHub: a-r-d/ai-json-safe-parse
npm: ai-json-safe-parse

If you hit a format it doesn't handle, open an issue with the raw text.

Top comments (0)