Build a Bounded JSON Repair Loop for LLM Output in Python A developer built a bounded JSON repair loop for LLM output in Python using only the standard library. The system classifies failures into syntax, shape, and semantic errors, and retries up to two times before raising an error. The approach emphasizes a clean control flow of generate, parse, validate, bounded repair, and accept or fail, enabling measurable validation rates. “Return JSON” is a request, not a guarantee. An LLM can produce invalid syntax, valid JSON with the wrong shape, or a structurally valid answer that violates the task. A repair loop should classify those failures and stop after a small number of attempts. This example uses only Python's standard library: python import json CATEGORIES = {"bug", "feature", "question"} def validate raw: str : try: value = json.loads raw except json.JSONDecodeError as exc: return None, f"syntax: {exc.msg} at position {exc.pos}" if not isinstance value, dict : return None, "shape: root must be an object" if set value = {"category", "summary"}: return None, "shape: expected category and summary only" if value "category" not in CATEGORIES: return None, "semantic: category is not allowed" if not isinstance value "summary" , str or not value "summary" .strip : return None, "semantic: summary must be non-empty text" return value, None Try three fixtures: samples = '{"category":"bug","summary":"Crash on save",}', '{"category":"bug","title":"Crash on save"}', '{"category":"urgent","summary":"Crash on save"}', for sample in samples: print validate sample 1 Expected categories are syntax , shape , and semantic . MAX ATTEMPTS = 2 raw = first model response for attempt in range MAX ATTEMPTS + 1 : value, error = validate raw if error is None: break if attempt == MAX ATTEMPTS: raise RuntimeError f"invalid model output: {error}" raw = ask model to repair raw, error your API adapter Send the original output, the exact validation error, and the required schema to the repair call. Do not add an expanding conversation history. It increases cost and can distract from the one correction needed. A parser can identify malformed output. It cannot decide whether “Crash on save” really describes a bug. Validate factual support against source evidence in a separate stage. Automatically repairing an unsupported claim can make it more polished without making it true. Also log attempt count, error class, model/version, and final disposition—but avoid logging sensitive prompts or full user data by default. The important pattern is not the particular schema. It is the control flow: php generate - parse - validate - bounded repair - accept or fail Once you can observe each transition, you can measure how often repair helps and when it only burns tokens. “Usually valid JSON” becomes a testable rate instead of a hopeful integration plan.