srbhr/Resume-Matcher · error · Error
Failed to update cover letter
Error message
Failed to update cover letter (status ${res.status}): ${text} What it means
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.
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.
Example fix
// before
await updateCoverLetter(resumeId, content);
// after
try {
await updateCoverLetter(resumeId, content);
} catch (err) {
if (String(err.message).includes('status 401')) {
await refreshSession();
await updateCoverLetter(resumeId, content);
} else {
notifyUser('Cover letter could not be saved. Your draft is kept locally.');
}
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!resumeId || typeof content !== 'string' || content.trim().length === 0) {
throw new Error('Cannot save cover letter: missing resumeId or empty content');
} Try / catch
try {
await updateCoverLetter(resumeId, content);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
const status = msg.match(/status (\d+)/)?.[1];
if (status === '401') queueReauthAndRetry(() => updateCoverLetter(resumeId, content));
else showToast(`Save failed (${status ?? 'unknown'}): draft kept locally`);
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Failed to rename resume
- Failed to update outreach message
- Failed to download cover letter
- Failed to generate cover letter
- ).}
AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28).
Data as JSON: /api/errors/98e1ec5cc298bd2c.
Report an issue: GitHub.
Appendix: source
Thrown at apps/frontend/lib/api/resume.ts:290
}
return await res.blob();
}
/** Deletes a resume by ID */
export async function deleteResume(resumeId: string): Promise<void> {
const res = await apiDelete(`/resumes/${encodeURIComponent(resumeId)}`);
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(`Failed to delete resume (status ${res.status}): ${text}`);
}
}
/** Updates the cover letter for a resume */
export async function updateCoverLetter(resumeId: string, content: string): Promise<void> {
const res = await apiPatch(`/resumes/${encodeURIComponent(resumeId)}/cover-letter`, { content });
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(`Failed to update cover letter (status ${res.status}): ${text}`);
}
}
/** Updates the outreach message for a resume */
export async function updateOutreachMessage(resumeId: string, content: string): Promise<void> {
const res = await apiPatch(`/resumes/${encodeURIComponent(resumeId)}/outreach-message`, {
content,
});
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(`Failed to update outreach message (status ${res.status}): ${text}`);
}
}
/** Renames a resume by updating its title */
export async function renameResume(resumeId: string, title: string): Promise<void> {
const res = await apiPatch(`/resumes/${encodeURIComponent(resumeId)}/title`, { title });
if (!res.ok) {View on GitHub (pinned to 116f9cc3b0)