{"record":{"id":"d51511678738b725","repo":"rohitg00/ai-engineering-from-scratch","slug":"invalid-json-at-character-exc-pos","errorCode":null,"errorMessage":"invalid JSON at character {exc.pos}","messagePattern":"invalid JSON at character (.+?)","errorType":"validation","errorClass":"ContractError","httpStatus":null,"severity":"error","filePath":"certifications/claude/lessons/09-structured-output-and-defensive-parsing/code/main.py","lineNumber":44,"sourceCode":"TRIAGE_SCHEMA: dict[str, Any] = {\n    \"type\": \"object\",\n    \"required\": [\"category\", \"priority\", \"summary\", \"needs_human\"],\n    \"additionalProperties\": False,\n    \"properties\": {\n        \"category\": {\"type\": \"string\", \"enum\": [\"billing\", \"bug\", \"account\", \"other\"]},\n        \"priority\": {\"type\": \"integer\", \"minimum\": 1, \"maximum\": 5},\n        \"summary\": {\"type\": \"string\", \"minLength\": 1, \"maxLength\": 240},\n        \"needs_human\": {\"type\": \"boolean\"},\n    },\n}\n\n\ndef parse_and_validate(raw: str, schema: dict[str, Any]) -> Any:\n    \"\"\"Accept exactly one JSON value, then validate the supported schema subset.\"\"\"\n    try:\n        value = json.loads(raw)\n    except json.JSONDecodeError as exc:\n        raise ContractError([ValidationIssue(\"$\", f\"invalid JSON at character {exc.pos}\")]) from exc\n    issues = validate(value, schema)\n    if issues:\n        raise ContractError(issues)\n    return value\n\n\ndef validate(value: Any, schema: dict[str, Any], path: str = \"$\") -> list[ValidationIssue]:\n    issues: list[ValidationIssue] = []\n    expected = schema.get(\"type\")\n    if expected == \"object\":\n        if not isinstance(value, dict):\n            return [ValidationIssue(path, \"expected object\")]\n        properties = schema.get(\"properties\", {})\n        for name in schema.get(\"required\", []):\n            if name not in value:\n                issues.append(ValidationIssue(f\"{path}.{name}\", \"required field is missing\"))\n        if schema.get(\"additionalProperties\") is False:\n            for name in value:","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/rohitg00/ai-engineering-from-scratch/blob/39ea8a1c6d0b61f071226eff7ede4d4105fed820/certifications/claude/lessons/09-structured-output-and-defensive-parsing/code/main.py#L26-L62","documentation":"Raised by parse_and_validate when the raw string is not valid JSON; the character position from json.JSONDecodeError is wrapped into a ContractError at path '$'. Syntax errors are reported as structured validation issues rather than raw exceptions so callers get a uniform error shape.","triggerScenarios":"Calling extract() or parse_and_validate() with malformed JSON: trailing commas, smart quotes, unescaped newlines in strings, markdown fences around the JSON, or a truncated model response.","commonSituations":"LLM output wrapped in ```json fences, model prose before or after the JSON, encoding damage from copy-paste, or a streamed response cut off mid-object.","solutions":["Prompt for raw JSON with no fences, or use forced tool/structured output so no prose is emitted","Extract the outermost JSON object from surrounding prose before parsing","Use the reported character position to locate and fix truncation or quote damage","If the cause is truncation, raise max_tokens and retry the generation"],"exampleFix":"# before\nvalue = parse_and_validate(\"```json\\n{\\\"a\\\": 1}\\n```\", schema)\n# after\nraw = '{\"a\": 1}'\nvalue = parse_and_validate(raw, schema)","handlingStrategy":"try-catch","validationCode":"import json\ndef looks_like_json(raw: str) -> bool:\n    s = raw.strip()\n    if not s.startswith((\"{\", \"[\")):\n        return False\n    try:\n        json.loads(s)\n        return True\n    except json.JSONDecodeError:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    value = parse_and_validate(raw, schema)\nexcept ContractError as exc:\n    for issue in exc.issues:\n        if issue.path == \"$\" and issue.message.startswith(\"invalid JSON\"):\n            raw = repair_or_reextract(raw)  # or re-prompt the model\n            value = parse_and_validate(raw, schema)","preventionTips":["Prompt for raw JSON without markdown fences, or use forced structured output","Use the reported character position as the repair anchor for truncation or quote issues","Retry the generation on truncation instead of hand-patching the string"],"tags":["json","structured-output","llm-parsing","python"],"backgroundTag":"invalid-json-from-llm","analyzedSha":"39ea8a1c6d0b61f071226eff7ede4d4105fed820","analyzedAt":"2026-08-26T03:13:46.626Z","schemaVersion":2},"datasetVersion":"2026-08-26T07:17:17.940Z"}