srbhr/Resume-Matcher · error · ValueError
Improved resume missing personalInfo
Error message
Improved resume missing personalInfo
What it means
The mirror check of the original-side null guard: the improved resume produced by the pipeline must carry a personalInfo object so it can be compared field-by-field against the original. If the improved data's personalInfo is None, the confirm endpoint raises this ValueError (surfaced as HTTP 400) rather than silently persisting a resume whose identity block was lost.
Source
Thrown at apps/backend/app/routers/resumes.py:536
return None, None, f"calculation_error: {str(e)}"
def _validate_confirm_payload(
original_data: dict[str, Any] | None,
improved_data: dict[str, Any],
) -> None:
if not original_data:
logger.warning(
"Skipping confirm payload validation; structured resume data unavailable."
)
return
original_info = original_data.get("personalInfo")
improved_info = improved_data.get("personalInfo")
# JSON-008: Explicit null checks with clear error messages
if original_info is None:
raise ValueError("Original resume missing personalInfo")
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)}")
View on GitHub (pinned to 116f9cc3b0)
Solutions
- Re-run POST /resumes/improve/preview so the safety nets (_preserve_personal_info etc.) regenerate a valid personalInfo block
- Echo the preview's improved data verbatim in the confirm request; never hand-build it
- Check improver/diff code changes if this reproduces deterministically (field block-list may be excluding personalInfo path)
Example fix
// before: confirm request with improved data missing the identity block
{"resume_id": "...", "job_id": "...", "improved_data": {"summary": "..."}}
// after: improved_data includes unchanged personalInfo copied from the preview
{"resume_id": "...", "job_id": "...", "improved_data": {"personalInfo": {"name": "Jane Doe"}, "summary": "..."}} Defensive patterns
Strategy: validation
Validate before calling
// client: echo the preview's improved data verbatim, never rebuild it
if (!payload.improved_data?.personalInfo) {
throw new Error('confirm payload must include personalInfo from the preview');
} Type guard
const hasPersonalInfo = (d: unknown): d is { personalInfo: Record<string, unknown> } =>
typeof d === 'object' && d !== null && 'personalInfo' in d && typeof (d as any).personalInfo === 'object' && (d as any).personalInfo !== null; Try / catch
try {
await api.post('/resumes/improve/confirm', body);
} catch (e) {
if (e.response?.status === 400) {
// re-run preview and retry with its canonical improved_data
const preview = await api.post('/resumes/improve/preview', previewPayload);
await api.post('/resumes/improve/confirm', buildConfirm(preview.data));
}
} Prevention
- Always construct the confirm body from the preview response, field-for-field
- Never let user edits touch the personalInfo block between preview and confirm
- Schema-validate improved_data (e.g. with the ResumeData schema) before POSTing
When it happens
Trigger: POST /resumes/improve/confirm where the preview/pipeline output (improved_data from processed/improved payload) has personalInfo missing or null — usually because an LLM or diff-application step dropped the field, or a client tampered with the improved payload.
Common situations: LLM returning JSON that omits personalInfo; legacy full-output improve path producing schema drift; client constructing a confirm request body manually instead of echoing the preview result.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- Original resume missing personalInfo
- personalInfo fields changed: {', '.join(mismatches)}
- Original personalInfo is not a dict: {type(original_info).__
- Improved personalInfo is not a dict: {type(improved_info).__
- ${message = data.detail or Failed to update LLM config (stat
AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28).
Data as JSON: /api/errors/4608b965f7f96cb3.
Report an issue: GitHub.