srbhr/Resume-Matcher · error · ValueError

personalInfo fields changed: {', '.join(mismatches)}

Error message

personalInfo fields changed: {', '.join(mismatches)}

What it means

The core immutability guarantee of the improve/confirm flow: after validating shape, _validate_confirm_payload unions the key sets of original and improved personalInfo, normalizes each value, and raises this ValueError listing every field whose value changed. It guarantees the tailoring pipeline never alters identity data (name, email, phone, location, links) — only experience/skills content. Surfaced as HTTP 400.

Source

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

    if improved_info is None:
        raise ValueError("Improved resume missing personalInfo")
    if not isinstance(original_info, dict):
        raise ValueError(
            f"Original personalInfo is not a dict: {type(original_info).__name__}"
        )
    if not isinstance(improved_info, dict):
        raise ValueError(
            f"Improved personalInfo is not a dict: {type(improved_info).__name__}"
        )
    fields = set(original_info.keys()) | set(improved_info.keys())
    mismatches = [
        field
        for field in sorted(fields)
        if _normalize_personal_info_value(original_info.get(field))
        != _normalize_personal_info_value(improved_info.get(field))
    ]
    if mismatches:
        raise ValueError(f"personalInfo fields changed: {', '.join(mismatches)}")


async def _generate_auxiliary_messages(
    improved_data: dict[str, Any],
    job_content: str,
    language: str,
    enable_cover_letter: bool,
    enable_outreach: bool,
    enable_interview_prep: bool,
) -> tuple[str | None, str | None, str | None, InterviewPrepData | None, list[str]]:
    """Generate cover letter, outreach, interview prep, and resume title.

    Returns (cover_letter, outreach_message, title, interview_prep, warnings).
    """
    cover_letter = None
    outreach_message = None
    title = None
    interview_prep = None

View on GitHub (pinned to 116f9cc3b0)

Solutions

  1. Copy the original personalInfo values into the improved payload before confirming — identity fields must be byte-identical after normalization
  2. Re-run preview and submit its improved data unchanged
  3. Check whether a custom diff allow-list or prompt change is letting the LLM edit personalInfo paths, and block that path

Example fix

// before: pipeline changed the email in improved_data
"personalInfo": {"name": "Jane Doe", "email": "jane@newmail.com"}
// after: identity block identical to the original resume
"personalInfo": {"name": "Jane Doe", "email": "jane@example.com"}
Defensive patterns

Strategy: validation

Validate before calling

// client: pre-flight parity check before confirm (mirrors the server's normalization)
function personalInfoChanged(orig, improved) {
  const norm = v => (v ?? '').toString().replace(/\s+/g, ' ').trim().toLowerCase();
  const keys = new Set([...Object.keys(orig), ...Object.keys(improved)]);
  return [...keys].filter(k => norm(orig[k]) !== norm(improved[k]));
}
const mismatches = personalInfoChanged(original.personalInfo, improvedData.personalInfo);
if (mismatches.length) throw new Error(`identity fields edited: ${mismatches}`);

Try / catch

try {
  await api.post('/resumes/improve/confirm', body);
} catch (e) {
  if (e.response?.status === 400 && /personalInfo fields changed/.test(e.response?.data?.detail ?? '')) {
    // restore original identity block and resubmit
    body.improved_data.personalInfo = structuredClone(original.personalInfo);
    await api.post('/resumes/improve/confirm', body);
  }
}

Prevention

When it happens

Trigger: POST /resumes/improve/confirm where any personalInfo field differs between the stored original and the submitted improved data — normalization differences like whitespace, case, or dash/date variants are folded away, so this fires only on real value changes (different email, missing phone, altered LinkedIn URL).

Common situations: LLM rewrote the identity block despite safety nets; a client (or script) modified personalInfo between preview and confirm; diff application edited personalInfo paths; the preview hash is fine but the payload drifted.

Related errors


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