{"record":{"id":"98e1ec5cc298bd2c","repo":"srbhr/Resume-Matcher","slug":"failed-to-update-cover-letter-status-res-status","errorCode":null,"errorMessage":"Failed to update cover letter (status ${res.status}): ${text}","messagePattern":"Failed to update cover letter \\(status (.+?)\\): (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/frontend/lib/api/resume.ts","lineNumber":290,"sourceCode":"  }\n  return await res.blob();\n}\n\n/** Deletes a resume by ID */\nexport async function deleteResume(resumeId: string): Promise<void> {\n  const res = await apiDelete(`/resumes/${encodeURIComponent(resumeId)}`);\n  if (!res.ok) {\n    const text = await res.text().catch(() => '');\n    throw new Error(`Failed to delete resume (status ${res.status}): ${text}`);\n  }\n}\n\n/** Updates the cover letter for a resume */\nexport async function updateCoverLetter(resumeId: string, content: string): Promise<void> {\n  const res = await apiPatch(`/resumes/${encodeURIComponent(resumeId)}/cover-letter`, { content });\n  if (!res.ok) {\n    const text = await res.text().catch(() => '');\n    throw new Error(`Failed to update cover letter (status ${res.status}): ${text}`);\n  }\n}\n\n/** Updates the outreach message for a resume */\nexport async function updateOutreachMessage(resumeId: string, content: string): Promise<void> {\n  const res = await apiPatch(`/resumes/${encodeURIComponent(resumeId)}/outreach-message`, {\n    content,\n  });\n  if (!res.ok) {\n    const text = await res.text().catch(() => '');\n    throw new Error(`Failed to update outreach message (status ${res.status}): ${text}`);\n  }\n}\n\n/** Renames a resume by updating its title */\nexport async function renameResume(resumeId: string, title: string): Promise<void> {\n  const res = await apiPatch(`/resumes/${encodeURIComponent(resumeId)}/title`, { title });\n  if (!res.ok) {","sourceCodeStart":272,"sourceCodeEnd":308,"githubUrl":"https://github.com/srbhr/Resume-Matcher/blob/116f9cc3b00e1ac91734a6c2679bf41ea64a0edc/apps/frontend/lib/api/resume.ts#L272-L308","documentation":"updateCoverLetter() PATCHes /resumes/:id/cover-letter via apiPatch. Any non-OK HTTP response (4xx/5xx) causes this throw. The server's error body text and status code are embedded in the message so the caller can see why the backend rejected the save.","triggerScenarios":"Calling updateCoverLetter(resumeId, content) when the API returns !res.ok: invalid/unknown resumeId (404), expired or missing auth session (401), validation rejection of the content payload (400/422), server error while persisting (500), or network/proxy failure surfaced as an HTTP error status.","commonSituations":"User edits a cover letter after the resume was deleted in another tab (404); JWT/session expired so the backend returns 401; content exceeds backend length limits or contains disallowed characters (400); backend deploy or DB outage causing 5xx during autosave.","solutions":["Inspect the embedded status and body text in the message to identify the exact cause (401 vs 404 vs 400).","If 401: refresh the session/token (re-login or token refresh) and retry the save.","If 404: verify resumeId is valid and the resume still exists before saving.","If 400/422: trim/validate the cover letter content against backend limits and re-save.","If 5xx: retry after a delay; check backend health/logs if it persists.","Persist unsaved content in local state so the user does not lose edits while fixing the cause."],"exampleFix":"// before\nawait updateCoverLetter(resumeId, content);\n\n// after\ntry {\n  await updateCoverLetter(resumeId, content);\n} catch (err) {\n  if (String(err.message).includes('status 401')) {\n    await refreshSession();\n    await updateCoverLetter(resumeId, content);\n  } else {\n    notifyUser('Cover letter could not be saved. Your draft is kept locally.');\n  }\n}","handlingStrategy":"try-catch","validationCode":"if (!resumeId || typeof content !== 'string' || content.trim().length === 0) {\n  throw new Error('Cannot save cover letter: missing resumeId or empty content');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await updateCoverLetter(resumeId, content);\n} catch (err) {\n  const msg = err instanceof Error ? err.message : String(err);\n  const status = msg.match(/status (\\d+)/)?.[1];\n  if (status === '401') queueReauthAndRetry(() => updateCoverLetter(resumeId, content));\n  else showToast(`Save failed (${status ?? 'unknown'}): draft kept locally`);\n}","preventionTips":["Validate resumeId and non-empty content before calling the API.","Handle 401 globally (interceptor) with token refresh before requests reach this throw.","Keep unsaved edits in local state/storage until save succeeds.","Retry transient 5xx with backoff instead of failing immediately."],"tags":["http","api","frontend","resume"],"backgroundTag":"http-non-ok-response","analyzedSha":"116f9cc3b00e1ac91734a6c2679bf41ea64a0edc","analyzedAt":"2026-08-28T22:51:40.999Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}