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
- 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
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
- 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
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
- Failed to delete application. Please try again.
- Failed to delete applications. Please try again.
- ${data.detail || Failed to reset database (status ${res.stat
- Resume not found: {resume_id}
- Failed to save changes. Please try again.
AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28).
Data as JSON: /api/errors/fc2c47753708a1d1.
Report an issue: GitHub.