srbhr/Resume-Matcher · error · HTTPException
Resume content changed or could not be uniquely matched. Ple
Error message
Resume content changed or could not be uniquely matched. Please regenerate and try again.
What it means
A 409 Conflict raised by apply_regenerated_items in the enrichment router. It means one or more item IDs the client sent back for applying AI-regenerated changes no longer uniquely matched the resume's current content (content changed, item deleted, or duplicate content made matching ambiguous). The endpoint deliberately refuses partial application so stale edits never overwrite newer resume state.
Source
Thrown at apps/backend/app/routers/enrichment.py:781
if not _lines_equal(additional.get("technicalSkills"), expected_original_content):
apply_failures.append(item_id)
continue
additional["technicalSkills"] = new_content
elif "technicalSkills" in updated_data:
# Fallback for legacy data structure
if not _lines_equal(updated_data.get("technicalSkills"), expected_original_content):
apply_failures.append(item_id)
continue
updated_data["technicalSkills"] = new_content
else:
apply_failures.append(item_id)
if apply_failures:
logger.warning(
"apply-regenerated: refusing to apply due to mismatched/missing items. "
f"resume_id={resume_id} item_ids={apply_failures}"
)
raise HTTPException(
status_code=409,
detail=(
"Resume content changed or could not be uniquely matched. "
"Please regenerate and try again."
),
)
# Update the resume in database
updated_content = json.dumps(updated_data, indent=2)
try:
await db.update_resume(
resume_id,
{
"content": updated_content,
"processed_data": updated_data,
},
)
except Exception as e:View on GitHub (pinned to 116f9cc3b0)
Solutions
- Regenerate the enrichment items (call the regenerate endpoint again) and immediately apply with the fresh payload
- Refetch the resume to confirm its updated_at/content hash matches what the regeneration was based on before applying
- Deduplicate item_ids in the request and ensure each original_content uniquely maps to one resume item
- Retry after refreshing the UI so the client holds current resume state
Example fix
// before: applying stale regenerated items
await api.post(`/enrichment/${resumeId}/apply-regenerated`, cachedPayload);
// after: refetch resume and abort if it changed since regeneration
const resume = await api.get(`/resumes/${resumeId}`);
if (resume.contentHash !== cachedPayload.baseContentHash) {
throw new StaleResumeError('Resume changed; regenerate before applying');
}
await api.post(`/enrichment/${resumeId}/apply-regenerated`, cachedPayload); Defensive patterns
Strategy: validation
Validate before calling
async function canApply(resumeId, payload) {
const resume = await api.get(`/resumes/${resumeId}`);
if (resume.contentHash !== payload.baseContentHash) return false;
const ids = new Set(payload.items.map(i => i.item_id));
if (ids.size !== payload.items.length) return false; // duplicates
return payload.items.every(i => typeof i.item_id === 'string' && i.item_id.length > 0);
} Type guard
function isValidRegenPayload(p) {
return Array.isArray(p?.items) && p.items.length > 0 &&
p.items.every(i => typeof i?.item_id === 'string' && typeof i?.original_content === 'string');
} Try / catch
try {
await api.post(`/enrichment/${resumeId}/apply-regenerated`, payload);
} catch (e) {
if (e.response?.status === 409) {
await regenerateAndReapply(resumeId); // stale: regenerate fresh items
} else throw e;
} Prevention
- Always apply immediately after regenerating; never cache regenerated payloads across user edits
- Send a content hash/updated_at from regeneration time and verify it before applying
- Deduplicate item_ids client-side to avoid ambiguous matches
- Refresh resume state before each apply call
When it happens
Trigger: POSTing the regenerated-items payload after the resume was edited/regenerated elsewhere; referencing item_ids that no longer exist; sending two items whose original content both match the same resume item (ambiguous duplicate); a concurrent apply request already mutated the items.
Common situations: User regenerates in a second tab while the first tab's apply is pending; client caches the regeneration response and applies it after a manual edit; the AI regeneration omitted/renamed item IDs so the client echoes stale or duplicated IDs; race between two apply calls.
Related errors
- A master resume already exists. Delete it before creating a
- Failed to save changes. Please try again.
- No job descriptions provided
- Empty job description
- Job not found
AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28).
Data as JSON: /api/errors/1950357509973412.
Report an issue: GitHub.