{"record":{"id":"fc2c47753708a1d1","repo":"srbhr/Resume-Matcher","slug":"failed-to-update-resume","errorCode":null,"errorMessage":"Failed to update resume","messagePattern":"Failed to update resume","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"critical","filePath":"apps/backend/app/routers/resumes.py","lineNumber":1552,"sourceCode":"    existing = await db.get_resume(resume_id)\n    if not existing:\n        raise HTTPException(status_code=404, detail=\"Resume not found\")\n\n    updated_data = resume_data.model_dump()\n    updated_content = json.dumps(updated_data, indent=2)\n\n    updated = await db.update_resume(\n        resume_id,\n        {\n            \"content\": updated_content,\n            \"content_type\": \"json\",\n            \"processed_data\": updated_data,\n            \"processing_status\": \"ready\",\n        },\n    )\n\n    if not updated:\n        raise HTTPException(status_code=500, detail=\"Failed to update resume\")\n\n    raw_resume = RawResume(\n        id=None,\n        content=updated[\"content\"],\n        content_type=updated[\"content_type\"],\n        created_at=updated[\"created_at\"],\n        processing_status=updated.get(\"processing_status\", \"pending\"),\n    )\n\n    processed_resume = (\n        ResumeData.model_validate(updated.get(\"processed_data\"))\n        if updated.get(\"processed_data\")\n        else None\n    )\n\n    return ResumeFetchResponse(\n        request_id=str(uuid4()),\n        data=ResumeFetchData(","sourceCodeStart":1534,"sourceCodeEnd":1570,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/backend/app/routers/resumes.py#L1534-L1570","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the update after re-checking the resume still exists","Inspect DB connectivity, logs, and pool health for write failures","Make the repository return the error cause instead of a bare falsy value to distinguish races from DB errors","Serialize delete vs update per resume (locking or soft-delete) to eliminate the race"],"exampleFix":"# before\nupdated = await db.update_resume(resume_id, ...)\nif not updated:\n    raise HTTPException(500, detail=\"Failed to update resume\")\n# after: re-check and retry once on race\nexisting = await db.get_resume(resume_id)\nif not existing:\n    raise HTTPException(404, detail=\"Resume not found\")\nupdated = await db.update_resume(resume_id, ...)","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"async function updateWithRetry(resumeId, data, attempts = 2) {\n  for (let i = 0; i < attempts; i++) {\n    try { return await updateResume(resumeId, data); }\n    catch (e) {\n      if (e.status === 500 && i < attempts - 1) {\n        const existing = await getResume(resumeId).catch(() => null);\n        if (!existing) throw new Error('Resume deleted concurrently');\n        await sleep(500); continue;\n      }\n      throw e;\n    }\n  }\n}","preventionTips":["Re-check existence before retrying a failed update to distinguish races from DB outages","Monitor database connection pool and replication health","Avoid concurrent delete+update on the same resume (locks or soft deletes)","Have the repository surface the underlying DB error, not a bare falsy return"],"tags":["http-500","database","race-condition","update-failed"],"backgroundTag":"database-write-failed","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}