{"record":{"id":"c22543be83cc2d0a","repo":"srbhr/Resume-Matcher","slug":"detail","errorCode":null,"errorMessage":"detail","messagePattern":"detail","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"apps/backend/app/routers/resumes.py","lineNumber":200,"sourceCode":"        return \"\"\n    if isinstance(value, str):\n        return unicodedata.normalize(\"NFC\", value).strip()\n    if isinstance(value, (int, float, bool)):\n        return str(value)\n    normalized = _normalize_payload(value)\n    return json.dumps(\n        normalized, sort_keys=True, separators=(\",\", \":\"), ensure_ascii=False\n    )\n\n\ndef _raise_improve_error(\n    action: str,\n    stage: str,\n    error: Exception,\n    detail: str,\n) -> NoReturn:\n    logger.error(\"Resume %s failed during %s: %s\", action, stage, error)\n    raise HTTPException(status_code=500, detail=detail)\n\n\ndef _get_original_resume_data(resume: dict[str, Any]) -> dict[str, Any] | None:\n    original_data = resume.get(\"processed_data\")\n    if not original_data and resume.get(\"content_type\") == \"json\":\n        try:\n            original_data = json.loads(resume[\"content\"])\n        except json.JSONDecodeError as e:\n            logger.warning(\"Skipping resume diff due to JSON parse failure: %s\", e)\n    return original_data\n\n\ndef _get_original_markdown(resume: dict[str, Any]) -> str | None:\n    \"\"\"Get the original markdown content from a resume.\n\n    Checks ``original_markdown`` first (persisted at upload), then\n    falls back to ``content`` if it's still in markdown format.\n    \"\"\"","sourceCodeStart":182,"sourceCodeEnd":218,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/backend/app/routers/resumes.py#L182-L218","documentation":"_raise_improve_error is the shared error funnel for the resume-improve endpoints (preview and confirm). It logs the underlying exception server-side with the action and stage, then re-raises a generic HTTPException with status 500 and a client-facing detail string. The 'detail' message is that client-facing string produced when a stage of the improve pipeline (LLM call, diff application, DB write, etc.) fails and the endpoint funnels the failure through this helper.","triggerScenarios":"Any exception inside improve_resume_preview_endpoint or improve_resume_confirm_endpoint that is caught and passed to _raise_improve_error: LLM completion failures/timeouts, diff generation or verification errors, database errors, or PDF/parsing steps raising mid-pipeline.","commonSituations":"LLM provider misconfigured or key missing/expired; local model (Ollama/llama.cpp) down or slow; 240s asyncio.wait_for timeout exceeded on a long preview; SQLite lock or schema mismatch; a malformed resume record causing a downstream service to throw.","solutions":["Check the backend log line 'Resume ... failed during <stage>: <error>' for the real underlying exception","Verify LLM config: provider, model, and API key are set and the provider is reachable (POST /config/llm-test)","Retry the preview; if it times out near 240s, reduce resume size or use a faster model","Re-upload/reprocess the resume (/{id}/retry-processing) if stored processed_data is corrupt"],"exampleFix":"// before: raw provider exception surfaced with no stage context\nexcept Exception as e:\n    raise HTTPException(status_code=500, detail=str(e))\n// after: logged server-side, generic detail to client\nexcept Exception as e:\n    _raise_improve_error(\"improve\", \"preview\", e, \"Resume improvement failed. Please try again.\")","handlingStrategy":"try-catch","validationCode":"// client: confirm LLM is healthy before an improve run\nconst st = await fetch('/api/v1/status').then(r => r.json());\nif (!st.llm_healthy) throw new Error('LLM not configured; fix config before improving');","typeGuard":null,"tryCatchPattern":"try {\n  await api.post('/resumes/improve/preview', payload);\n} catch (e) {\n  if (e.response?.status === 500) {\n    console.warn('Improve failed server-side; see backend logs for stage');\n    // offer user a retry rather than a hard failure\n  }\n}","preventionTips":["Keep resume size moderate; the preview has a 240s hard timeout","Validate LLM provider/key/model with POST /config/llm-test before long runs","Read the server log line (action + stage + error) — the client detail is intentionally generic","Re-process old resumes (/{id}/retry-processing) if stored processed_data may be stale or corrupt"],"tags":["http-500","improve-pipeline","llm","fastapi"],"backgroundTag":"generic-500-from-pipeline-failure","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}