{"record":{"id":"60186ddf4f6a8c0b","repo":"srbhr/Resume-Matcher","slug":"resume-preview-data-is-invalid","errorCode":null,"errorMessage":"Resume preview data is invalid.","messagePattern":"Resume preview data is invalid\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/frontend/app/(default)/tailor/page.tsx","lineNumber":130,"sourceCode":"    };\n\n    loadPromptConfig();\n    return () => {\n      cancelled = true;\n    };\n  }, []);\n\n  const handleTextareaKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n    if (e.key === 'Enter') e.stopPropagation();\n  };\n\n  const buildConfirmPayload = (result: ImprovedResult) => {\n    if (!masterResumeId) {\n      throw new Error('Master resume ID is missing.');\n    }\n    const resumePreview = result.data.resume_preview;\n    if (!resumePreview || typeof resumePreview !== 'object' || Array.isArray(resumePreview)) {\n      throw new Error('Resume preview data is invalid.');\n    }\n    const previewRecord = resumePreview as unknown as Record<string, unknown>;\n    if (\n      !previewRecord.personalInfo ||\n      typeof previewRecord.personalInfo !== 'object' ||\n      Array.isArray(previewRecord.personalInfo)\n    ) {\n      throw new Error('Resume preview data is invalid.');\n    }\n    return {\n      resume_id: masterResumeId,\n      job_id: result.data.job_id,\n      improved_data: resumePreview as ResumeData,\n      improvements:\n        result.data.improvements?.map((item) => ({\n          suggestion: item.suggestion,\n          lineNumber: typeof item.lineNumber === 'number' ? item.lineNumber : null,\n        })) ?? [],","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/frontend/app/(default)/tailor/page.tsx#L112-L148","documentation":"buildConfirmPayload throws this when result.data.resume_preview is missing, not an object, or an array. resume_preview is the AI-generated improved resume returned by the improve.preview flow; the page requires a plain object before it can be validated further and cast to ResumeData. The check exists because the LLM response may be malformed or the field may be absent, and the payload would otherwise send garbage to improve.confirm.","triggerScenarios":"Confirming an improved result where result.data.resume_preview is undefined/null (backend omitted the field), is a JSON string instead of a parsed object, is an array, or the ImprovedResult was deserialized from a truncated/failed API response.","commonSituations":"LLM providers returning malformed JSON that the backend passes through partially; an older backend version not producing resume_preview; a proxy/response interceptor wrapping or stringifying the data field; tests feeding hand-built fixtures without resume_preview.","solutions":["Regenerate the improved preview (re-run the improve flow) so the backend returns a valid resume_preview object.","Log the full ImprovedResult to inspect what shape resume_preview actually arrived in (string vs object vs array).","If the backend returns it as a JSON string, JSON.parse it before the check (or fix the backend serializer).","Check backend version/compatibility: upgrade or fix the /improve/preview endpoint so it always emits resume_preview as an object."],"exampleFix":"// before\nconst resumePreview = result.data.resume_preview;\nif (!resumePreview || typeof resumePreview !== 'object' || Array.isArray(resumePreview)) {\n  throw new Error('Resume preview data is invalid.');\n}\n// after\nlet raw = result.data.resume_preview;\nif (typeof raw === 'string') {\n  try { raw = JSON.parse(raw); } catch { raw = null; }\n}\nif (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n  throw new Error('Resume preview data is invalid.');\n}\nconst resumePreview = raw;","handlingStrategy":"validation","validationCode":"function isValidResumePreview(v: unknown): boolean {\n  return (\n    !!v &&\n    typeof v === 'object' &&\n    !Array.isArray(v) &&\n    Object.keys(v as Record<string, unknown>).length > 0\n  );\n}\n// run before calling buildConfirmPayload / confirmAndNavigate","typeGuard":"function isResumePreview(v: unknown): v is Record<string, unknown> {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  await confirmAndNavigate(result);\n} catch (err) {\n  if (err instanceof Error && err.message === 'Resume preview data is invalid.') {\n    setModalError('The generated preview was malformed — regenerate and try again.');\n    return;\n  }\n  throw err;\n}","preventionTips":["Validate the API response shape right after improve.preview returns, before showing the diff modal.","Log/inspect result.data when the LLM output looks odd; check whether resume_preview arrived as a JSON string.","Keep backend serialization and frontend ResumeData types in sync (camelCase keys).","Add a fixture test that feeds a result without resume_preview and asserts the error path."],"tags":["validation","llm","api-response","schema","client-side"],"backgroundTag":"schema-validation-failed","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}