srbhr/Resume-Matcher · error · HTTPException
Invalid improved resume data. Please retry preview.
Error message
Invalid improved resume data. Please retry preview.
What it means
improve_resume_confirm_endpoint raises HTTP 400 when _validate_confirm_payload (comparing the improved payload against the original resume data) raises ValueError, meaning the client sent improved data inconsistent with the stored original or malformed relative to what preview produced.
Source
Thrown at apps/backend/app/routers/resumes.py:1149
feature_config = _load_config()
enable_cover_letter = feature_config.get("enable_cover_letter", False)
enable_outreach = feature_config.get("enable_outreach_message", False)
enable_interview_prep = feature_config.get("enable_interview_prep", False)
language = get_content_language()
stage = "serialize_improved_data"
detail = "Failed to confirm resume. Please try again."
try:
improved_data = request.improved_data.model_dump()
improved_text = json.dumps(improved_data, indent=2)
# NOTE: This endpoint relies on preview-hash validation to ensure the payload matches a prior preview.
# Stronger guarantees would require server-side preview storage or re-running the improvement.
try:
_validate_confirm_payload(_get_original_resume_data(resume), improved_data)
except ValueError as e:
logger.warning("Resume confirm rejected: %s", e)
raise HTTPException(
status_code=400,
detail="Invalid improved resume data. Please retry preview.",
)
preview_hashes = job.get("preview_hashes")
allowed_hashes: set[str] = set()
if isinstance(preview_hashes, dict):
allowed_hashes.update(preview_hashes.values())
elif isinstance(preview_hashes, list):
allowed_hashes.update(
[value for value in preview_hashes if isinstance(value, str)]
)
else:
preview_hash = job.get("preview_hash")
if isinstance(preview_hash, str):
allowed_hashes.add(preview_hash)
if not allowed_hashes:
logger.warning(View on GitHub (pinned to 116f9cc3b0)
Solutions
- Re-run the preview and confirm with the preview response's improved data unmodified (round-trip the exact object)
- Clear stale client state (cached preview payload) and start the tailoring flow again
- Check backend version mismatch: if the server was updated between preview and confirm, redo preview
- Inspect backend logs ('Resume confirm rejected: ...') to see which validation rule failed
Example fix
// before
const edited = {...previewData.improved};
edited.sections.push(newSection); // tampered
confirm({resume_id, job_id, improved_data: edited});
// after
confirm({resume_id, job_id, improved_data: previewData.improved}); // exact payload Defensive patterns
Strategy: validation
Validate before calling
function isUntouchedPreview(preview: {improved: object}, toSend: object): boolean {
// send the exact preview payload — deep-equal check before confirm
return JSON.stringify(preview.improved) === JSON.stringify(toSend);
}
if (!isUntouchedPreview(preview, payload.improved_data)) payload.improved_data = preview.improved; Type guard
function looksLikeImprovedResume(d: unknown): d is Record<string, unknown> {
return typeof d === 'object' && d !== null &&
Array.isArray((d as any).sections) && (d as any).sections.length > 0 &&
typeof (d as any).contact === 'object';
} Try / catch
try {
await api.improveConfirm({resume_id, job_id, improved_data});
} catch (e) {
if (e.response?.status === 400 && /Invalid improved resume data/.test(e.response.data?.detail ?? '')) {
const fresh = await api.improvePreview({resume_id, job_id});
await api.improveConfirm({resume_id, job_id, improved_data: fresh.improved});
} else throw e;
} Prevention
- Never mutate the preview response before sending it back — round-trip the exact object
- Avoid hand-building confirm payloads; derive them from the preview response
- Restart preview→confirm after any backend upgrade between the two calls
- Read the 'Resume confirm rejected' warning in backend logs to learn which rule failed
When it happens
Trigger: POST to improve/confirm whose improved_data fails payload validation — edited/tampered sections between preview and confirm, a changed schema, or confirming with hand-built JSON instead of the exact preview response body.
Common situations: Frontend mutated the preview response (e.g. injected a new section or rewrote IDs) before confirming; backend was upgraded mid-flow so validation rules differ; client retries confirm with an old payload against a newer backend.
Related errors
- Preview required before confirmation. Please retry preview.
- Cover letter can only be generated for tailored resumes. Ple
- Unsupported UI language: {request.ui_language}. Supported: {
- Unsupported content language: {request.content_language}. Su
- Unsupported prompt id: {request.default_prompt_id}. Supporte
AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28).
Data as JSON: /api/errors/a1e9b84b8e13635c.
Report an issue: GitHub.