srbhr/Resume-Matcher · critical · HTTPException

Failed to update resume

Error message

Failed to update resume

What it means

HTTP 500 raised by update_resume_endpoint when db.update_resume(...) returns a falsy result, meaning the existence check passed but the update itself failed to persist. Indicates a race with deletion, a DB write failure, or a driver/repository returning None on error.

Source

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

    existing = await db.get_resume(resume_id)
    if not existing:
        raise HTTPException(status_code=404, detail="Resume not found")

    updated_data = resume_data.model_dump()
    updated_content = json.dumps(updated_data, indent=2)

    updated = await db.update_resume(
        resume_id,
        {
            "content": updated_content,
            "content_type": "json",
            "processed_data": updated_data,
            "processing_status": "ready",
        },
    )

    if not updated:
        raise HTTPException(status_code=500, detail="Failed to update resume")

    raw_resume = RawResume(
        id=None,
        content=updated["content"],
        content_type=updated["content_type"],
        created_at=updated["created_at"],
        processing_status=updated.get("processing_status", "pending"),
    )

    processed_resume = (
        ResumeData.model_validate(updated.get("processed_data"))
        if updated.get("processed_data")
        else None
    )

    return ResumeFetchResponse(
        request_id=str(uuid4()),
        data=ResumeFetchData(

View on GitHub (pinned to 116f9cc3b0)

Solutions

  1. Retry the update after re-checking the resume still exists
  2. Inspect DB connectivity, logs, and pool health for write failures
  3. Make the repository return the error cause instead of a bare falsy value to distinguish races from DB errors
  4. Serialize delete vs update per resume (locking or soft-delete) to eliminate the race

Example fix

# before
updated = await db.update_resume(resume_id, ...)
if not updated:
    raise HTTPException(500, detail="Failed to update resume")
# after: re-check and retry once on race
existing = await db.get_resume(resume_id)
if not existing:
    raise HTTPException(404, detail="Resume not found")
updated = await db.update_resume(resume_id, ...)
Defensive patterns

Strategy: retry

Try / catch

async function updateWithRetry(resumeId, data, attempts = 2) {
  for (let i = 0; i < attempts; i++) {
    try { return await updateResume(resumeId, data); }
    catch (e) {
      if (e.status === 500 && i < attempts - 1) {
        const existing = await getResume(resumeId).catch(() => null);
        if (!existing) throw new Error('Resume deleted concurrently');
        await sleep(500); continue;
      }
      throw e;
    }
  }
}

Prevention

When it happens

Trigger: Resume deleted by another request between get_resume and update_resume (TOCTOU race), database connectivity/timeout during the write, or repository update returning None due to an unmatched filter or internal error.

Common situations: Concurrent deletes and edits in a multi-tab UI, transient DB outages (connection pool exhaustion), replica failover mid-write, or wrong collection/table mapping after a migration.

Related errors


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