{"record":{"id":"cc126d5d8730c2a0","repo":"srbhr/Resume-Matcher","slug":"improved-personalinfo-is-not-a-dict-type-improve","errorCode":null,"errorMessage":"Improved personalInfo is not a dict: {type(improved_info).__name__}","messagePattern":"Improved personalInfo is not a dict: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"apps/backend/app/routers/resumes.py","lineNumber":542,"sourceCode":") -> None:\n    if not original_data:\n        logger.warning(\n            \"Skipping confirm payload validation; structured resume data unavailable.\"\n        )\n        return\n    original_info = original_data.get(\"personalInfo\")\n    improved_info = improved_data.get(\"personalInfo\")\n    # JSON-008: Explicit null checks with clear error messages\n    if original_info is None:\n        raise ValueError(\"Original resume missing personalInfo\")\n    if improved_info is None:\n        raise ValueError(\"Improved resume missing personalInfo\")\n    if not isinstance(original_info, dict):\n        raise ValueError(\n            f\"Original personalInfo is not a dict: {type(original_info).__name__}\"\n        )\n    if not isinstance(improved_info, dict):\n        raise ValueError(\n            f\"Improved personalInfo is not a dict: {type(improved_info).__name__}\"\n        )\n    fields = set(original_info.keys()) | set(improved_info.keys())\n    mismatches = [\n        field\n        for field in sorted(fields)\n        if _normalize_personal_info_value(original_info.get(field))\n        != _normalize_personal_info_value(improved_info.get(field))\n    ]\n    if mismatches:\n        raise ValueError(f\"personalInfo fields changed: {', '.join(mismatches)}\")\n\n\nasync def _generate_auxiliary_messages(\n    improved_data: dict[str, Any],\n    job_content: str,\n    language: str,\n    enable_cover_letter: bool,","sourceCodeStart":524,"sourceCodeEnd":560,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/backend/app/routers/resumes.py#L524-L560","documentation":"JSON-008 type guard on the improved side: improved_data.personalInfo must be a dict to allow field-by-field comparison with the original. If it is present but not a dict, the ValueError embeds the actual type name and the confirm endpoint surfaces HTTP 400. This prevents persisting a tailored resume whose identity block has the wrong structure.","triggerScenarios":"POST /resumes/improve/confirm where the improved payload's personalInfo is a non-dict (string, list, etc.) — typically a client-sent malformed confirm body, or an LLM/diff step emitting personalInfo as an array of strings.","commonSituations":"Hand-crafted confirm requests in scripts/tests; LLM JSON-mode output shape drift; automated tooling posting transformed data without schema validation.","solutions":["Validate the improved payload shape client-side before calling confirm (personalInfo must be an object)","Re-run preview and use its improved data verbatim","If caused by the pipeline, inspect generate_resume_diffs/apply_diffs output for personalInfo path corruption"],"exampleFix":"// before: LLM emitted a list\n\"personalInfo\": [\"Jane Doe\", \"jane@example.com\"]\n// after\n\"personalInfo\": {\"name\": \"Jane Doe\", \"email\": \"jane@example.com\"}","handlingStrategy":"type-guard","validationCode":"// client: validate the improved payload before confirm\nfunction validPersonalInfo(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\nif (!validPersonalInfo(improvedData.personalInfo)) throw new TypeError('personalInfo must be an object');","typeGuard":"function isPlainObject(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  await api.post('/resumes/improve/confirm', body);\n} catch (e) {\n  if (e.response?.status === 400) {\n    const preview = await api.post('/resumes/improve/preview', previewPayload);\n    // retry using the canonical preview output\n  }\n}","preventionTips":["Schema-check LLM output shape before submitting confirm (arrays-as-personalInfo is the classic drift)","Never hand-assemble confirm bodies in scripts","Add a JSON-schema assertion on improved_data in your test suite"],"tags":["validation","type-error","personal-info","schema"],"backgroundTag":"schema-validation-failed","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}