{"record":{"id":"e55a795f68d61d54","repo":"srbhr/Resume-Matcher","slug":"job-description-is-only-available-for-tailored-res","errorCode":null,"errorMessage":"Job description is only available for tailored resumes.","messagePattern":"Job description is only available for tailored resumes\\.","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"apps/backend/app/routers/resumes.py","lineNumber":1993,"sourceCode":"        message=\"Interview preparation generated successfully\",\n    )\n\n\n@router.get(\"/{resume_id}/job-description\")\nasync def get_job_description_for_resume(resume_id: str) -> dict:\n    \"\"\"Get the job description used to tailor this resume.\n\n    This endpoint retrieves the original job description that was used\n    to tailor a resume. Only works for tailored resumes (those with parent_id).\n    \"\"\"\n    # Get the resume\n    resume = await db.get_resume(resume_id)\n    if not resume:\n        raise HTTPException(status_code=404, detail=\"Resume not found\")\n\n    # Check if it's a tailored resume (has parent_id)\n    if not resume.get(\"parent_id\"):\n        raise HTTPException(\n            status_code=400,\n            detail=\"Job description is only available for tailored resumes.\",\n        )\n\n    # Get improvement record to find the job_id\n    improvement = await db.get_improvement_by_tailored_resume(resume_id)\n    if not improvement:\n        raise HTTPException(\n            status_code=400,\n            detail=\"No job context found for this resume. \"\n            \"The resume may have been created before job tracking was implemented.\",\n        )\n\n    # Get the job description\n    job = await db.get_job(improvement[\"job_id\"])\n    if not job:\n        raise HTTPException(\n            status_code=404,","sourceCodeStart":1975,"sourceCodeEnd":2011,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/backend/app/routers/resumes.py#L1975-L2011","documentation":"HTTP 400 raised when the resume exists but has no parent_id, meaning it is an original (untailored) resume. Job descriptions are only stored for tailored resumes created from a job context, so the endpoint rejects originals by design.","triggerScenarios":"Request the job description for a resume that was uploaded directly (not produced by the tailoring flow), so resume.parent_id is absent/null.","commonSituations":"UI showing the 'view job description' action on non-tailored resumes; client confusing original vs tailored resume ids after duplication; older data model without parent_id backfilled as null.","solutions":["Only call this endpoint for tailored resumes — check parent_id in the resume object before calling.","If you need a job link for an original resume, tailor it against a job first, then use the tailored copy's id.","Hide/disable the job-description action in the UI when parent_id is missing."],"exampleFix":"// before\nconst res = await fetch(`/api/resumes/${resume.id}/job-description`);\n// after\nif (resume.parent_id) {\n  const res = await fetch(`/api/resumes/${resume.id}/job-description`);\n}","handlingStrategy":"type-guard","validationCode":"const resume = await getResume(id);\nif (!resume?.parent_id) {\n  throw new SkipError('not a tailored resume — job description endpoint not applicable');\n}","typeGuard":"function isTailoredResume(r) {\n  return r != null && typeof r.parent_id === 'string' && r.parent_id.length > 0;\n}","tryCatchPattern":"try {\n  const jd = await getJobDescription(resumeId);\n} catch (e) {\n  if (e.status === 400 && /tailored resumes/i.test(e.detail)) {\n    return null; // original resume: no job description exists by design\n  }\n  throw e;\n}","preventionTips":["Check parent_id before showing any 'view job description' UI action.","Label resumes as original vs tailored in the client data model.","Understand the endpoint contract: it is scoped to tailored resumes only."],"tags":["http-400","business-rule","fastapi","client-error"],"backgroundTag":"operation-not-applicable","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}