srbhr/Resume-Matcher · error · Error

Failed to update outreach message (status ${res.status}): ${

Error message

Failed to update outreach message (status ${res.status}): ${text}

What it means

updateOutreachMessage() PATCHes /resumes/:id/outreach-message via apiPatch. Any non-OK HTTP response (4xx/5xx) causes this throw with the status and response body embedded in the message. It is the outreach-message counterpart of the cover-letter save.

Source

Thrown at apps/frontend/lib/api/resume.ts:301

}

/** 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) {
    const text = await res.text().catch(() => '');
    throw new Error(`Failed to rename resume (status ${res.status}): ${text}`);
  }
}

/** Downloads cover letter as PDF */
export function getCoverLetterPdfUrl(
  resumeId: string,
  pageSize: 'A4' | 'LETTER' = 'A4',
  locale?: Locale
): string {

View on GitHub (pinned to 116f9cc3b0)

Solutions

  1. Read the status code and body text from the message to pinpoint the failure.
  2. If 401: re-authenticate (token refresh or re-login), then retry the update.
  3. If 404: confirm the resumeId exists and is correct.
  4. If 400/422: validate content length/format against the backend contract and re-save.
  5. If 5xx: retry with backoff; escalate to backend logs if persistent.
  6. Keep the edited text in component state until the save succeeds.

Example fix

// before
await updateOutreachMessage(resumeId, content);

// after
try {
  await updateOutreachMessage(resumeId, content);
} catch (err) {
  console.error('Outreach save failed:', err.message);
  setDraft(content); // preserve user input
  notifyUser('Save failed — check your session and try again.');
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!resumeId || typeof content !== 'string' || content.length > MAX_OUTREACH_LENGTH) {
  throw new Error('Cannot save outreach message: invalid resumeId or oversized content');
}

Try / catch

try {
  await updateOutreachMessage(resumeId, content);
} catch (err) {
  const msg = err instanceof Error ? err.message : String(err);
  if (/status 40[13]/.test(msg)) triggerRelogin();
  else if (/status 5\d\d/.test(msg)) scheduleRetry(() => updateOutreachMessage(resumeId, content));
  else notifyUser(`Outreach save failed: ${msg}`);
}

Prevention

When it happens

Trigger: Calling updateOutreachMessage(resumeId, content) when the API returns !res.ok: stale resumeId (404), expired auth (401), content rejected by validation (400/422), or backend persistence failure (500).

Common situations: Session token expired mid-editing, so the save gets 401; resume deleted or ID malformed leading to 404; outreach text exceeds backend size limits causing 400; transient 502/503 from an API gateway during deploy.

Related errors


AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28). Data as JSON: /api/errors/99dbd895d4ad00ab. Report an issue: GitHub.