<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: mtdevworks</title>
    <description>The latest articles on Forem by mtdevworks (@mtdevworks).</description>
    <link>https://forem.com/mtdevworks</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3754405%2F4b83ae71-d048-4617-ac87-cb7723404cf4.png</url>
      <title>Forem: mtdevworks</title>
      <link>https://forem.com/mtdevworks</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mtdevworks"/>
    <language>en</language>
    <item>
      <title>I Built an API for LLM JSON Validation in Rust — Here’s What I Learned</title>
      <dc:creator>mtdevworks</dc:creator>
      <pubDate>Mon, 09 Feb 2026 14:41:05 +0000</pubDate>
      <link>https://forem.com/mtdevworks/i-built-an-api-for-llm-json-validation-in-rust-heres-what-i-learned-36nc</link>
      <guid>https://forem.com/mtdevworks/i-built-an-api-for-llm-json-validation-in-rust-heres-what-i-learned-36nc</guid>
      <description>&lt;p&gt;I kept hitting the same wall: LLM outputs breaking production. Trailing commas, unquoted keys, JSON wrapped in markdown—every deploy felt like a game of whack-a-mole. Prompt engineering helped a bit, but I didn’t want to spend the next year tuning “please return valid JSON” for every new feature. So I built &lt;strong&gt;JSON Guardian&lt;/strong&gt;: an API that validates, repairs, and enforces JSON from LLM outputs. This post is about the technical choices, the hard parts, and what I’d tell myself on day one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why an API instead of “just fix the prompts”?
&lt;/h2&gt;

&lt;p&gt;Prompts can improve things, but they don’t fix the underlying issue: LLMs are trained on messy data and they’re not deterministic. You can ask for JSON and still get prose, markdown, or invalid syntax. I wanted a single place that sits between the model and my app—something that always returns either valid, schema-conforming data or a clear error. That’s easier to reason about than scattering retries and regex across the codebase.&lt;/p&gt;

&lt;p&gt;I also wanted it to work from &lt;strong&gt;any&lt;/strong&gt; stack: Node, Python, n8n, whatever. So I built an API, not a library. You get a key, you send HTTP requests, you get back structured success or failure. No language lock-in, no “install this SDK first.”&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Rust?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Performance.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This layer runs on every LLM response. If it adds 50–100ms, users notice. I was aiming for &lt;strong&gt;sub-10ms&lt;/strong&gt; p99 so that validation feels free compared to the model call. Rust made that realistic: no GC pauses, predictable latency, and the ability to tune hot paths without fighting a runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory safety and no runtime surprises.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
No null-pointer or type surprises at runtime. The compiler catches a lot of bugs before they hit production. For a service that parses untrusted LLM output, that’s a big deal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trust and reliability.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Rust’s ecosystem gave me what I needed without having to build a JSON parser or a web server from scratch. I could focus on the validation and repair logic instead of fighting the runtime. For a closed-source service, that means we can keep the implementation details in-house while still being able to talk about why we chose Rust: speed, safety, and predictable behaviour.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture in a nutshell
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API layer:&lt;/strong&gt; HTTP, async, built for the “one request → validate/repair → response” model. Kept simple so latency stays predictable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validation:&lt;/strong&gt; JSON Schema Draft 7. Same standard everyone knows; easy to document and reuse.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storage:&lt;/strong&gt; A database for API keys and usage tracking. The service stays stateless per request; state lives in the backend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endpoints:&lt;/strong&gt; Validate, repair, enforce (repair + schema + type coercion), extract (strip JSON from prose/markdown), partial (complete streaming JSON), batch (any operation on many items). One concern per endpoint so callers can compose what they need.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Validate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Check JSON against schema&lt;/td&gt;
&lt;td&gt;Strict validation only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repair&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fix common syntax errors&lt;/td&gt;
&lt;td&gt;Malformed JSON cleanup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enforce&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Repair + schema + type coercion&lt;/td&gt;
&lt;td&gt;Fixing and conforming LLM output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Extract&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Strip JSON from prose/markdown&lt;/td&gt;
&lt;td&gt;Raw model responses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Partial&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Complete streaming JSON&lt;/td&gt;
&lt;td&gt;Real-time UI updates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Batch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bulk operation on many items&lt;/td&gt;
&lt;td&gt;High-volume processing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I kept deployment simple: single region, no Kubernetes. Latency stays low because the service is small and the runtime is predictable.&lt;/p&gt;




&lt;h2&gt;
  
  
  The hard parts
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Parsing malformed JSON.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can’t validate what you can’t parse. So the repair step had to come first: fix trailing commas, single quotes, unquoted keys, and then feed the result into the schema validator. Getting the repair logic right without over-correcting (or breaking valid edge cases) took most of the early iteration. I had to draw a line: fix obvious syntax, don’t guess at semantics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to repair vs reject.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Too strict and you reject fixable output; too loose and you “fix” things into wrong data. I ended up with a small, well-defined set of repairs (trailing commas, quote normalization, etc.) and left the rest to validation. If repair can’t produce valid JSON, we return an error with a clear message instead of guessing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type coercion.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The enforce endpoint can coerce types—e.g. &lt;code&gt;"twenty-five"&lt;/code&gt; → &lt;code&gt;25&lt;/code&gt; for integer fields. Useful for LLM output; dangerous if overdone. I limited it to a few patterns (numbers, booleans, missing required with defaults) and made the response include a &lt;code&gt;changes_made&lt;/code&gt; list so callers see what was altered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Partial/streaming JSON.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Completing half-written JSON (e.g. for real-time UI during streaming) is a different problem from “is this string valid?” I added a dedicated partial endpoint that closes strings and brackets in a minimal way. It’s best-effort: great for display, but I don’t use it as the source of truth until the stream is done.&lt;/p&gt;




&lt;h2&gt;
  
  
  Launch: direct API + RapidAPI
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Direct API:&lt;/strong&gt; &lt;a href="https://api.jsonguardian.com" rel="noopener noreferrer"&gt;https://api.jsonguardian.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
For speed and control. Sub-10ms when you call us directly; no proxy in the middle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RapidAPI:&lt;/strong&gt; &lt;a href="https://rapidapi.com/mtdevworks2025/api/json-guardian" rel="noopener noreferrer"&gt;https://rapidapi.com/mtdevworks2025/api/json-guardian&lt;/a&gt;&lt;br&gt;&lt;br&gt;
For distribution and discovery. Same API, same behaviour; we accept RapidAPI headers and track those calls separately. Good for reaching people who already browse the marketplace.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Choice&lt;/th&gt;
&lt;th&gt;Speed&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Discovery&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Direct API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sub-10ms p99&lt;/td&gt;
&lt;td&gt;Maximum control &amp;amp; lowest latency&lt;/td&gt;
&lt;td&gt;Manual signup at jsonguardian.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RapidAPI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Same via proxy&lt;/td&gt;
&lt;td&gt;Marketplace reach &amp;amp; existing users&lt;/td&gt;
&lt;td&gt;Browse RapidAPI marketplace&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Pricing:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Free tier 10k requests/month (no card), then Starter (100k) and Pro (1M). Batch requests count each item as one. I kept the free tier generous so people can try it in real workflows without worrying about the first mistake.&lt;/p&gt;

&lt;p&gt;Early feedback so far: people care about &lt;strong&gt;speed&lt;/strong&gt; (“is it really under 10ms?”) and &lt;strong&gt;docs&lt;/strong&gt; (“can I see the exact request/response?”). So I doubled down on the public API docs and the OpenAPI spec. The dashboard at &lt;a href="https://jsonguardian.com" rel="noopener noreferrer"&gt;jsonguardian.com&lt;/a&gt; handles signup and usage so you can see your own numbers.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I’d do again (and what I’d change)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ship fast, then iterate.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
I could have spent months polishing the repair heuristics. Instead I shipped a small set of repairs and added more as real payloads showed up. That was the right call. You learn more from real usage than from hypothetical edge cases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation is part of the product.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The API is useless if people can’t figure out the body shape and error format. PUBLIC_README, OpenAPI, and a few copy-paste examples (Node, Python, curl) took real time but made the API feel “just works.” I’d do that from day one next time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First 10 users &amp;gt; perfect product.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
I’m optimising for the first 10–20 teams that actually use it. Their feedback (what broke, what they expected, what they’d pay for) is worth more than another week of tuning type coercion. So: ship, share in communities, and listen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I’d change:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
I’d add integration examples (e.g. n8n, LangChain) earlier. A lot of interest comes from “I use X, how do I plug this in?” A short “use with n8n” or “use with OpenAI function calling” post would have saved back-and-forth. I’m doing that now.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you’re tired of LLM JSON breaking your app, you can try JSON Guardian with no credit card:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Site and signup:&lt;/strong&gt; &lt;a href="https://jsonguardian.com" rel="noopener noreferrer"&gt;jsonguardian.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct API:&lt;/strong&gt; &lt;a href="https://api.jsonguardian.com" rel="noopener noreferrer"&gt;api.jsonguardian.com&lt;/a&gt; (health: &lt;a href="https://api.jsonguardian.com/health" rel="noopener noreferrer"&gt;api.jsonguardian.com/health&lt;/a&gt;)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RapidAPI:&lt;/strong&gt; &lt;a href="https://rapidapi.com/mtdevworks2025/api/json-guardian" rel="noopener noreferrer"&gt;rapidapi.com/mtdevworks2025/api/json-guardian&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Free tier: 10,000 requests/month. If you build something with it, I’d love to hear what worked and what didn’t—drop a comment or reach out.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;JSON Guardian: &lt;a href="https://jsonguardian.com" rel="noopener noreferrer"&gt;jsonguardian.com&lt;/a&gt; · &lt;a href="https://rapidapi.com/mtdevworks2025/api/json-guardian" rel="noopener noreferrer"&gt;RapidAPI&lt;/a&gt; · Free tier: 10k requests/month&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>webdev</category>
      <category>rust</category>
    </item>
    <item>
      <title>Building Reliable AI Applications: A Validation Strategy</title>
      <dc:creator>mtdevworks</dc:creator>
      <pubDate>Sun, 08 Feb 2026 10:53:49 +0000</pubDate>
      <link>https://forem.com/mtdevworks/building-reliable-ai-applications-a-validation-strategy-3b0f</link>
      <guid>https://forem.com/mtdevworks/building-reliable-ai-applications-a-validation-strategy-3b0f</guid>
      <description>&lt;p&gt;AI is unreliable by design. It hallucinates, drifts off-prompt, and—when you ask for structured output—often returns JSON that doesn’t parse or doesn’t match your schema. In production, one bad response can break a flow, confuse a user, or trigger a support ticket. So if you’re building with LLMs, validation isn’t optional; it’s part of the architecture.&lt;/p&gt;

&lt;p&gt;This post is a practical validation strategy: why it matters, what kinds of validation you need, where to put it, and how to keep it fast and maintainable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why validation matters
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Production failures are expensive.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A parsing error in a critical path can mean a failed checkout, a broken dashboard, or a silent wrong answer. Downtime and rollbacks cost time and trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;User trust is fragile.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
One visible bug—“Sorry, something went wrong”—undermines confidence. Users don’t care that the model returned a trailing comma; they care that your app didn’t handle it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Examples of what goes wrong:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parse error:&lt;/strong&gt; The LLM returns &lt;code&gt;{"name": "John",}&lt;/code&gt;. Your code does &lt;code&gt;JSON.parse(llmOutput)&lt;/code&gt; and crashes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong type:&lt;/strong&gt; The schema says &lt;code&gt;age&lt;/code&gt; is a number; the LLM returns &lt;code&gt;"age": "twenty-five"&lt;/code&gt;. Your validator fails or your downstream logic breaks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing field:&lt;/strong&gt; You require &lt;code&gt;email&lt;/code&gt;. The model omits it. You either fail the request or pass incomplete data into your system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Validation catches these &lt;em&gt;before&lt;/em&gt; they reach business logic. The goal is to either get valid, schema-conforming data or fail in a controlled, debuggable way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Types of validation
&lt;/h2&gt;

&lt;p&gt;Not all validation is the same. It helps to separate three layers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Syntax validation — Is it valid JSON?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Can you parse it? No trailing commas, no unquoted keys, no single quotes. If this fails, you don’t have a data structure at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Schema validation — Does it match the shape?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Given a JSON Schema (or similar), does the parsed object have the right properties and types? Are required fields present? This is where you enforce the contract between the LLM and your app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Semantic validation — Does it make sense?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Domain rules: “email must look like an email,” “date must be in the future,” “status must be one of these enums.” This is usually custom logic in your code, after syntax and schema are satisfied.&lt;/p&gt;

&lt;p&gt;For most LLM pipelines, &lt;strong&gt;syntax + schema&lt;/strong&gt; are the foundation. Get those right first; add semantic checks where needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where to validate: the middleware layer
&lt;/h2&gt;

&lt;p&gt;The right place for syntax and schema validation is &lt;strong&gt;between the LLM and your application&lt;/strong&gt;—a thin middleware layer that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receives the raw LLM output (string).&lt;/li&gt;
&lt;li&gt;Optionally &lt;strong&gt;extracts&lt;/strong&gt; JSON from prose or markdown.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repairs&lt;/strong&gt; syntax if possible (trailing commas, quotes, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validates&lt;/strong&gt; (or &lt;strong&gt;enforces&lt;/strong&gt;) against your JSON Schema.&lt;/li&gt;
&lt;li&gt;Returns either valid data or a clear error.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your app then consumes only validated, typed data. You don’t scatter &lt;code&gt;try/catch&lt;/code&gt; and regex across the codebase; you have one place that either succeeds or fails with a structured error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance matters.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This layer runs on every LLM response. If it adds 100ms, users notice. Aim for &lt;strong&gt;sub-10ms&lt;/strong&gt; so that validation is effectively free compared to the LLM call. That usually means a dedicated service (or a very fast library) rather than a heavyweight script.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error handling.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Decide your policy: &lt;strong&gt;retry&lt;/strong&gt; (e.g. once with a “fix your JSON” prompt), &lt;strong&gt;repair&lt;/strong&gt; (if the service can fix syntax and enforce schema), or &lt;strong&gt;fail fast&lt;/strong&gt; with a clear error to the user or to your monitoring. A repair step often gives the best balance: fewer failed requests, fewer retries, happier users.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tools and approaches
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;DIY: regex and custom parsers.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can strip trailing commas, normalize quotes, and even extract JSON from markdown with regex and a bit of parsing. It works for simple cases but gets brittle: edge cases, nested structures, and escape sequences. Maintenance cost is high.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Libraries: JSON Schema validators.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Use a solid validator (e.g. Ajv, &lt;code&gt;jsonschema&lt;/code&gt;) to check parsed JSON against a schema. Great for “is this valid?” They don’t repair; they don’t extract. So you still need to fix syntax and strip prose yourself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API services: validation + repair + extraction.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Offload the whole pipeline to a service that accepts raw LLM output and returns valid, schema-conforming data (or a clear error). You keep your code simple and get repair, extraction, and enforcement in one place. Latency stays low if the API is built for speed (e.g. Rust, edge deployment).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://jsonguardian.com" rel="noopener noreferrer"&gt;JSON Guardian&lt;/a&gt;&lt;/strong&gt; is built for this flow. You send the raw string; you get back:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Validate&lt;/strong&gt; — Check against JSON Schema (Draft 7).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repair&lt;/strong&gt; — Fix trailing commas, single quotes, unquoted keys, and similar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforce&lt;/strong&gt; — Repair first, then enforce schema with type coercion (e.g. &lt;code&gt;"twenty-five"&lt;/code&gt; → &lt;code&gt;25&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extract&lt;/strong&gt; — Pull JSON out of markdown or prose.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partial&lt;/strong&gt; — Complete partial/streaming JSON for real-time UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch&lt;/strong&gt; — Run any of the above on multiple items in one request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you can do: &lt;em&gt;extract → repair → enforce&lt;/em&gt; in sequence, or call a single endpoint that fits your step. Free tier: 10,000 requests/month at &lt;a href="https://jsonguardian.com" rel="noopener noreferrer"&gt;jsonguardian.com&lt;/a&gt;; also on &lt;a href="https://rapidapi.com/mtdevworks2025/api/json-guardian" rel="noopener noreferrer"&gt;RapidAPI&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Why&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Avoid production failures and protect user trust; one validation layer catches parse and schema errors.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;What&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Syntax validation (valid JSON), schema validation (shape + types), then semantic rules if needed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Where&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Between LLM and app—a thin middleware that extracts, repairs, validates/enforces.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;How&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Keep it fast (sub-10ms); choose retry, repair, or fail-fast; prefer a dedicated service over DIY for repair + schema.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Validation is non-negotiable for production AI. Start with syntax and schema; add extraction and streaming support as your use cases grow. Your future self (and your users) will thank you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Try JSON Guardian: &lt;a href="https://jsonguardian.com" rel="noopener noreferrer"&gt;jsonguardian.com&lt;/a&gt; · &lt;a href="https://rapidapi.com/mtdevworks2025/api/json-guardian" rel="noopener noreferrer"&gt;RapidAPI&lt;/a&gt; · Free tier: 10k requests/month&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>llm</category>
      <category>developers</category>
    </item>
    <item>
      <title>5 Ways LLMs Break JSON in Production (And How to Fix It)</title>
      <dc:creator>mtdevworks</dc:creator>
      <pubDate>Sat, 07 Feb 2026 08:29:09 +0000</pubDate>
      <link>https://forem.com/mtdevworks/5-ways-llms-break-json-in-production-and-how-to-fix-it-252e</link>
      <guid>https://forem.com/mtdevworks/5-ways-llms-break-json-in-production-and-how-to-fix-it-252e</guid>
      <description>&lt;p&gt;You've wired up GPT function calling or hooked LangChain into your app. Everything works in testing - until you deploy. Suddenly, you're seeing Unexpected token in JSON at position 42, or your schema validator rejects half the responses. Sound familiar?&lt;/p&gt;

&lt;p&gt;LLMs are great at &lt;em&gt;meaning&lt;/em&gt;, but they’re surprisingly bad at &lt;em&gt;syntax&lt;/em&gt;. Training data is full of inconsistent JSON, and models often mix it with JavaScript, YAML, or plain prose. The result is broken JSON that breaks your app.&lt;/p&gt;

&lt;p&gt;Here are the five most common ways LLMs break JSON—and practical ways to fix them, including an API that handles all of these automatically.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Trailing commas
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That comma after &lt;code&gt;30&lt;/code&gt; is invalid in JSON. In JavaScript it’s fine; in JSON it’s not. &lt;code&gt;JSON.parse()&lt;/code&gt; throws.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Models see both valid JSON and JavaScript in training data. They don’t always distinguish. Trailing commas also appear in arrays: &lt;code&gt;[1, 2, 3,]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to fix:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Strip trailing commas before the closing &lt;code&gt;}&lt;/code&gt; or &lt;code&gt;]&lt;/code&gt;, or run the string through a repair step that normalizes this. If you use a validation layer, choose one that can &lt;em&gt;repair&lt;/em&gt; as well as validate—so you get valid JSON out instead of just an error.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Unquoted keys
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;age:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Valid in JavaScript; invalid in JSON. Keys must be double-quoted strings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
LLMs are heavily trained on JavaScript/TypeScript. Object literals with unquoted keys are everywhere. The model reproduces that style.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to fix:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A repair step can wrap unquoted keys in double quotes: &lt;code&gt;name&lt;/code&gt; → &lt;code&gt;"name"&lt;/code&gt;. Regex can handle simple cases; for nested structures and edge cases, a dedicated parser/repairer is safer.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. Missing required fields
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Your schema says &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;age&lt;/code&gt; are required. The LLM returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;age&lt;/code&gt;. Your validator fails, and your app doesn’t know whether to retry, default, or show an error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Context limits, vague instructions, or the model “forgetting” part of the schema. It’s a semantic/schema problem, not just syntax.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to fix:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Two approaches: (1) &lt;strong&gt;Strict validation&lt;/strong&gt; — reject and retry or show a clear error. (2) &lt;strong&gt;Enforcement&lt;/strong&gt; — fix what you can (e.g. repair syntax), then enforce the schema with defaults for missing required fields. Enforcement is useful when you’d rather have a best-effort result than a hard failure.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. Mixed or single quotes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;'name':&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'John'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'John'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JSON allows only double quotes for strings and keys. Single quotes are invalid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Training data includes Python dicts, shell-style strings, and other formats. The model mixes quote styles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to fix:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Normalize to double quotes. Be careful with apostrophes inside strings (e.g. &lt;code&gt;"John's car"&lt;/code&gt;) so you don’t break them when converting. A repair layer that understands string boundaries handles this correctly.&lt;/p&gt;


&lt;h2&gt;
  
  
  5. JSON buried in prose or markdown
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Sure! Here's the data you asked for:

'```

json
{"name": "John", "age": 30}


```'

Hope that helps!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your code expects a raw JSON string. Instead you get a paragraph, markdown fences, and maybe extra text before or after. &lt;code&gt;JSON.parse()&lt;/code&gt; on the whole thing fails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it happens:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
LLMs are conversational. They explain, wrap code in markdown, and add pleasantries. That’s helpful for readability and terrible for parsing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to fix:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Extract the JSON first: strip markdown code fences, find the first &lt;code&gt;{&lt;/code&gt; or &lt;code&gt;[&lt;/code&gt;, then parse to the matching &lt;code&gt;}&lt;/code&gt; or &lt;code&gt;]&lt;/code&gt;, or use a dedicated “extract JSON from prose” step. Only then validate or repair. Doing extraction before validation keeps your pipeline robust.&lt;/p&gt;


&lt;h2&gt;
  
  
  A single layer that handles all five
&lt;/h2&gt;

&lt;p&gt;Fixing each of these by hand (regex, custom parsers, retry logic) gets messy fast. A cleaner approach is to put a small validation-and-repair layer between your LLM and your app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://jsonguardian.com" rel="noopener noreferrer"&gt;JSON Guardian&lt;/a&gt;&lt;/strong&gt; is an API built for exactly this: it validates, repairs, and enforces JSON from LLM outputs in under 10ms.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Trailing commas / unquoted keys / single quotes&lt;/strong&gt; → &lt;code&gt;POST /api/v1/repair&lt;/code&gt; returns valid JSON.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing required fields&lt;/strong&gt; → &lt;code&gt;POST /api/v1/enforce&lt;/code&gt; with your JSON Schema repairs syntax, then enforces the schema (including defaults for missing required fields when applicable).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSON in prose or markdown&lt;/strong&gt; → &lt;code&gt;POST /api/v1/extract&lt;/code&gt; strips fences and surrounding text and returns the extracted JSON.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You send the raw LLM response; you get back something you can safely parse and pass to the rest of your app. Built in Rust, so latency stays low—important when you’re calling it on every LLM response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick example — repair:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.jsonguardian.com/api/v1/repair &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"data": "{\"name\": \"John\", \"age\": 30,}"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Response includes a &lt;code&gt;repaired&lt;/code&gt; string and &lt;code&gt;repaired_data&lt;/code&gt; object—ready to use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick example — extract from markdown:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.jsonguardian.com/api/v1/extract &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-API-Key: YOUR_KEY"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"data": "Here is the result: ```

json\n{\"name\": \"John\"}\n

``` Hope that helps!"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get &lt;code&gt;extracted&lt;/code&gt; and &lt;code&gt;extracted_data&lt;/code&gt; without the surrounding text.&lt;/p&gt;

&lt;p&gt;Free tier: 10,000 requests/month. No credit card required. You can try it at &lt;strong&gt;&lt;a href="https://jsonguardian.com" rel="noopener noreferrer"&gt;jsonguardian.com&lt;/a&gt;&lt;/strong&gt; or via &lt;strong&gt;&lt;a href="https://rapidapi.com/mtdevworks2025/api/json-guardian" rel="noopener noreferrer"&gt;RapidAPI&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fespumae5p0kzsk8u7nqh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fespumae5p0kzsk8u7nqh.png" alt=" " width="686" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Handling these in one place—between the LLM and your business logic—keeps your app stable and your code simple. If you’re tired of debugging JSON parse errors in production, give a validation layer a try.&lt;/p&gt;

&lt;p&gt;What JSON issues are you running into with your LLM projects? I’d love to hear in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Try JSON Guardian: &lt;a href="https://jsonguardian.com" rel="noopener noreferrer"&gt;jsonguardian.com&lt;/a&gt; · &lt;a href="https://rapidapi.com/mtdevworks2025/api/json-guardian" rel="noopener noreferrer"&gt;RapidAPI&lt;/a&gt; · Free tier: 10k requests/month&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>json</category>
      <category>developer</category>
    </item>
  </channel>
</rss>
