{"slug": "build-a-bounded-json-repair-loop-for-llm-output-in-python", "title": "Build a Bounded JSON Repair Loop for LLM Output in Python", "summary": "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.", "body_md": "“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.\n\nA repair loop should classify those failures and stop after a small number of attempts.\n\nThis example uses only Python's standard library:\n\n``` python\nimport json\n\nCATEGORIES = {\"bug\", \"feature\", \"question\"}\n\ndef validate(raw: str):\n    try:\n        value = json.loads(raw)\n    except json.JSONDecodeError as exc:\n        return None, f\"syntax: {exc.msg} at position {exc.pos}\"\n\n    if not isinstance(value, dict):\n        return None, \"shape: root must be an object\"\n    if set(value) != {\"category\", \"summary\"}:\n        return None, \"shape: expected category and summary only\"\n    if value[\"category\"] not in CATEGORIES:\n        return None, \"semantic: category is not allowed\"\n    if not isinstance(value[\"summary\"], str) or not value[\"summary\"].strip():\n        return None, \"semantic: summary must be non-empty text\"\n\n    return value, None\n```\n\nTry three fixtures:\n\n```\nsamples = [\n    '{\"category\":\"bug\",\"summary\":\"Crash on save\",}',\n    '{\"category\":\"bug\",\"title\":\"Crash on save\"}',\n    '{\"category\":\"urgent\",\"summary\":\"Crash on save\"}',\n]\n\nfor sample in samples:\n    print(validate(sample)[1])\n```\n\nExpected categories are `syntax`\n\n, `shape`\n\n, and `semantic`\n\n.\n\n```\nMAX_ATTEMPTS = 2\n\nraw = first_model_response\nfor attempt in range(MAX_ATTEMPTS + 1):\n    value, error = validate(raw)\n    if error is None:\n        break\n    if attempt == MAX_ATTEMPTS:\n        raise RuntimeError(f\"invalid model output: {error}\")\n    raw = ask_model_to_repair(raw, error)  # your API adapter\n```\n\nSend 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.\n\nA 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.\n\nAlso log attempt count, error class, model/version, and final disposition—but avoid logging sensitive prompts or full user data by default.\n\nThe important pattern is not the particular schema. It is the control flow:\n\n``` php\ngenerate -> parse -> validate -> bounded repair -> accept or fail\n```\n\nOnce 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.", "url": "https://wpnews.pro/news/build-a-bounded-json-repair-loop-for-llm-output-in-python", "canonical_source": "https://dev.to/magickong/build-a-bounded-json-repair-loop-for-llm-output-in-python-1dm4", "published_at": "2026-07-15 11:22:10+00:00", "updated_at": "2026-07-15 11:30:39.035633+00:00", "lang": "en", "topics": ["developer-tools", "large-language-models", "natural-language-processing"], "entities": [], "alternates": {"html": "https://wpnews.pro/news/build-a-bounded-json-repair-loop-for-llm-output-in-python", "markdown": "https://wpnews.pro/news/build-a-bounded-json-repair-loop-for-llm-output-in-python.md", "text": "https://wpnews.pro/news/build-a-bounded-json-repair-loop-for-llm-output-in-python.txt", "jsonld": "https://wpnews.pro/news/build-a-bounded-json-repair-loop-for-llm-output-in-python.jsonld"}}