srbhr/Resume-Matcher · error · HTTPException

Preview required before confirmation. Please retry preview.

Error message

Preview required before confirmation. Please retry preview.

What it means

improve_resume_confirm_endpoint raises HTTP 400 when the job record has no preview_hashes (or the stored preview_hashes dict yields an empty allowed set), meaning no preview was ever run — or its hashes were never persisted — for this resume/job pair before confirmation was attempted.

Source

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

        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(
                "Rejecting confirm; preview hash missing for job %s.",
                request.job_id,
            )
            raise HTTPException(
                status_code=400,
                detail="Preview required before confirmation. Please retry preview.",
            )

        request_hash = _hash_improved_data(improved_data)
        if request_hash not in allowed_hashes:
            logger.warning("Resume confirm rejected due to preview hash mismatch.")
            raise HTTPException(
                status_code=400,
                detail="Invalid improved resume data. Please retry preview.",
            )

        stage = "calculate_diff"
        response_warnings: list[str] = []
        diff_summary, detailed_changes, diff_error = _calculate_diff_from_resume(
            resume,
            improved_data,
        )

View on GitHub (pinned to 116f9cc3b0)

Solutions

  1. Run the preview endpoint first and wait for a successful response, then confirm immediately with the same resume_id/job_id pair
  2. If preview previously timed out, raise REQUEST_TIMEOUT_SECONDS / NEXT_PUBLIC_REQUEST_TIMEOUT_MS and redo preview before confirming
  3. After a DB reset or backend redeploy, restart the whole preview→confirm flow
  4. Ensure the confirm request uses the same job_id that the successful preview used (hashes are stored per job)

Example fix

// before
confirm({resume_id, job_id}); // never previewed
// after
const preview = await api.preview({resume_id, job_id}); // succeeds and stores hashes
await api.confirm({resume_id, job_id, improved_data: preview.improved});
Defensive patterns

Strategy: validation

Validate before calling

async function canConfirm(resumeId: string, jobId: string): Promise<boolean> {
  const job = await fetch(`/api/jobs/${jobId}`).then(r => r.json()).catch(() => null);
  return !!job?.preview_hashes && Object.keys(job.preview_hashes).length > 0;
}
if (!(await canConfirm(resumeId, jobId))) await api.improvePreview({resume_id, job_id: jobId});

Type guard

function hasPreviewHashes(job: unknown): job is {preview_hashes: Record<string, string>} {
  return typeof job === 'object' && job !== null &&
    typeof (job as any).preview_hashes === 'object' && (job as any).preview_hashes !== null &&
    Object.keys((job as any).preview_hashes).length > 0;
}

Try / catch

try {
  await api.improveConfirm({resume_id, job_id, improved_data});
} catch (e) {
  if (e.response?.status === 400 && /Preview required/.test(e.response.data?.detail ?? '')) {
    const preview = await api.improvePreview({resume_id, job_id});
    await api.improveConfirm({resume_id, job_id, improved_data: preview.improved});
  } else throw e;
}

Prevention

When it happens

Trigger: POST to improve/confirm where job.preview_hashes is missing, not a dict, or empty: confirming without ever calling preview, after a DB reset that wiped job metadata, or after preview failed/timed out so hashes were never stored.

Common situations: User bookmarks the confirm step and replays it later; preview timed out (error 124/125) but the client still tries confirm; backend redeploy wiped in-memory/DB preview state; preview was run for a different job_id.

Related errors


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