srbhr/Resume-Matcher · error · HTTPException

detail

Error message

detail

What it means

_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.

Solutions

  1. Check the backend log line 'Resume ... failed during <stage>: <error>' for the real underlying exception
  2. Verify LLM config: provider, model, and API key are set and the provider is reachable (POST /config/llm-test)
  3. Retry the preview; if it times out near 240s, reduce resume size or use a faster model
  4. Re-upload/reprocess the resume (/{id}/retry-processing) if stored processed_data is corrupt

Example fix

// before: raw provider exception surfaced with no stage context
except Exception as e:
    raise HTTPException(status_code=500, detail=str(e))
// after: logged server-side, generic detail to client
except Exception as e:
    _raise_improve_error("improve", "preview", e, "Resume improvement failed. Please try again.")
Defensive patterns

Strategy: try-catch

Validate before calling

// client: confirm LLM is healthy before an improve run
const st = await fetch('/api/v1/status').then(r => r.json());
if (!st.llm_healthy) throw new Error('LLM not configured; fix config before improving');

Try / catch

try {
  await api.post('/resumes/improve/preview', payload);
} catch (e) {
  if (e.response?.status === 500) {
    console.warn('Improve failed server-side; see backend logs for stage');
    // offer user a retry rather than a hard failure
  }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28). Data as JSON: /api/errors/c22543be83cc2d0a. Report an issue: GitHub.

Appendix: source

Thrown at apps/backend/app/routers/resumes.py:200

        return ""
    if isinstance(value, str):
        return unicodedata.normalize("NFC", value).strip()
    if isinstance(value, (int, float, bool)):
        return str(value)
    normalized = _normalize_payload(value)
    return json.dumps(
        normalized, sort_keys=True, separators=(",", ":"), ensure_ascii=False
    )


def _raise_improve_error(
    action: str,
    stage: str,
    error: Exception,
    detail: str,
) -> NoReturn:
    logger.error("Resume %s failed during %s: %s", action, stage, error)
    raise HTTPException(status_code=500, detail=detail)


def _get_original_resume_data(resume: dict[str, Any]) -> dict[str, Any] | None:
    original_data = resume.get("processed_data")
    if not original_data and resume.get("content_type") == "json":
        try:
            original_data = json.loads(resume["content"])
        except json.JSONDecodeError as e:
            logger.warning("Skipping resume diff due to JSON parse failure: %s", e)
    return original_data


def _get_original_markdown(resume: dict[str, Any]) -> str | None:
    """Get the original markdown content from a resume.

    Checks ``original_markdown`` first (persisted at upload), then
    falls back to ``content`` if it's still in markdown format.
    """

View on GitHub (pinned to 116f9cc3b0)