{"record":{"id":"1f1c65d722e656fa","repo":"srbhr/Resume-Matcher","slug":"json-extraction-exceeded-max-recursion-depth-de","errorCode":null,"errorMessage":"JSON extraction exceeded max recursion depth: {_depth}","messagePattern":"JSON extraction exceeded max recursion depth: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"apps/backend/app/llm.py","lineNumber":1108,"sourceCode":"    tag. Strip these so JSON extraction finds the real output.\n    \"\"\"\n    # Remove <think>...</think> blocks (including multiline)\n    stripped = re.sub(r\"<think>.*?</think>\", \"\", content, flags=re.DOTALL)\n    # Also handle unclosed <think> tag (model may still be \"thinking\" at end)\n    stripped = re.sub(r\"<think>.*\", \"\", stripped, flags=re.DOTALL)\n    return stripped.strip()\n\n\ndef _extract_json(content: str, _depth: int = 0) -> str:\n    \"\"\"Extract JSON from LLM response, handling various formats.\n\n    LLM-001: Improved to detect and reject likely truncated JSON.\n    LLM-007: Improved error messages for debugging.\n    JSON-010: Added recursion depth and size limits.\n    \"\"\"\n    # JSON-010: Safety limits\n    if _depth > MAX_JSON_EXTRACTION_RECURSION:\n        raise ValueError(\n            f\"JSON extraction exceeded max recursion depth: {_depth}\")\n    if len(content) > MAX_JSON_CONTENT_SIZE:\n        raise ValueError(\n            f\"Content too large for JSON extraction: {len(content)} bytes\")\n\n    original = content\n\n    # Strip thinking model tags (deepseek-r1, qwq, etc.)\n    if \"<think>\" in content:\n        content = _strip_thinking_tags(content)\n\n    # Remove markdown code blocks\n    if \"```json\" in content:\n        content = content.split(\"```json\")[1].split(\"```\")[0]\n    elif \"```\" in content:\n        parts = content.split(\"```\")\n        if len(parts) >= 2:\n            content = parts[1]","sourceCodeStart":1090,"sourceCodeEnd":1126,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/backend/app/llm.py#L1090-L1126","documentation":"_extract_json in llm.py recursively parses/extracts JSON out of LLM text. To prevent stack exhaustion on pathological model output, it enforces MAX_JSON_EXTRACTION_RECURSION and raises this ValueError once _depth exceeds the limit.","triggerScenarios":"complete_json receives LLM output whose structure drives the recursive extraction past MAX_JSON_EXTRACTION_RECURSION — typically heavily nested or adversarial/pathological JSON-ish content.","commonSituations":"Model returns extremely deeply nested JSON; prompt-injected content designed to blow up the parser; a loop where the same bad response is re-extracted recursively.","solutions":["Retry the request asking the model for flatter, simpler JSON output","Reduce the requested payload size/complexity in the prompt (fewer nested objects)","If legitimately needed, raise MAX_JSON_EXTRACTION_RECURSION in llm.py","Pre-sanitize/validate the model output shape with a schema validator before deep extraction"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"def json_depth_ok(s: str, limit: int = 32) -> bool:\n    depth, in_str, esc = 0, False, False\n    for c in s:\n        if in_str:\n            if esc: esc = False\n            elif c == '\\\\': esc = True\n            elif c == '\"': in_str = False\n        elif c == '\"': in_str = True\n        elif c in '{[': depth += 1;  depth = depth\n        elif c in '}]': depth -= 1\n        if depth > limit: return False\n    return True","typeGuard":null,"tryCatchPattern":"try:\n    data = await complete_json(prompt)\nexcept ValueError as e:\n    if \"max recursion depth\" in str(e):\n        data = await complete_json(prompt + \" Keep the JSON structure flat, max 3 levels deep.\")\n    else:\n        raise","preventionTips":["Request flat JSON schemas in prompts (avoid deeply nested structures)","Sanitize/validate model output shape before deep parsing","Keep MAX_JSON_EXTRACTION_RECURSION as a deliberate guard rather than disabling it","Treat repeated depth failures as a prompt-injection signal"],"tags":["llm","json","recursion-limit","backend"],"backgroundTag":"json-recursion-limit-exceeded","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}