srbhr/Resume-Matcher · error · Error
Failed to update resume (status ${res.status}): ${text}
Error message
Failed to update resume (status ${res.status}): ${text} What it means
updateResume saves edited resume data (ProcessedResume) via apiPatch to /resumes/{id}. On a non-OK response it reads the response body as text and throws this Error including both the status and the server's error message, making it the most diagnostic of the resume read/write errors. It means the backend rejected the resume update.
Source
Thrown at apps/frontend/lib/api/resume.ts:220
}
export async function fetchResumeList(includeMaster = false): Promise<ResumeListItem[]> {
const res = await apiFetch(`/resumes/list?include_master=${includeMaster ? 'true' : 'false'}`);
if (!res.ok) {
throw new Error(`Failed to load resumes list (status ${res.status}).`);
}
const payload = (await res.json()) as { data: ResumeListItem[] };
return payload.data;
}
export async function updateResume(
resumeId: string,
resumeData: ProcessedResume
): Promise<ResumeResponse['data']> {
const res = await apiPatch(`/resumes/${encodeURIComponent(resumeId)}`, resumeData);
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(`Failed to update resume (status ${res.status}): ${text}`);
}
const payload = (await res.json()) as ResumeResponse;
return payload.data;
}
export function getResumePdfUrl(
resumeId: string,
settings?: TemplateSettings,
locale?: Locale
): string {
const normalizedId = normalizeResumeId(resumeId);
const params = new URLSearchParams();
if (settings) {
params.set('template', settings.template);
params.set('pageSize', settings.pageSize);
params.set('marginTop', String(settings.margins.top));
params.set('marginBottom', String(settings.margins.bottom));View on GitHub (pinned to 116f9cc3b0)
Solutions
- Read the ${text} portion of the message — it contains the backend's validation/error detail; fix the offending fields in resumeData.
- Verify the resumeId still exists (fetchResumeList) before saving; re-sync UI state if it was deleted elsewhere.
- Confirm the ProcessedResume shape matches the current backend contract after any API version change.
- In runSave, catch the error, surface the server message to the user, and preserve local edits so they are not lost.
Example fix
// before
await updateResume(resumeId, resumeData);
// after
try {
await updateResume(resumeId, resumeData);
} catch (e) {
const msg = (e as Error).message;
console.error('Resume update rejected:', msg); // includes server body
notifyUser(`Save failed: ${msg}`);
} Defensive patterns
Strategy: validation
Validate before calling
function isValidProcessedResume(r: ProcessedResume): boolean {
return typeof r === 'object' && r !== null && 'id' in r;
}
if (!isValidProcessedResume(resumeData)) throw new Error('Invalid resume payload before PATCH'); Type guard
function isProcessedResume(x: unknown): x is ProcessedResume {
return typeof x === 'object' && x !== null && 'id' in x;
} Try / catch
try {
await updateResume(resumeId, resumeData);
} catch (e) {
const detail = (e as Error).message.split(':').slice(1).join(':').trim();
notifyUser(`Save failed: ${detail || 'unknown server error'}`);
} Prevention
- Validate ProcessedResume against the backend schema (zod/JSON schema) before PATCHing.
- Surface the server body text from the error message to users — it names the failing field.
- Re-check the resume still exists before save to avoid 404s after concurrent deletes.
- Regenerate/refresh typed API models whenever the backend contract changes.
When it happens
Trigger: apiPatch(`/resumes/${encodeURIComponent(resumeId)}`, resumeData) returns non-2xx: 422/400 from a ProcessedResume payload failing backend schema validation, 404 for an unknown resumeId, 401/403 for auth issues, or 409/500 from concurrent-edit or persistence failures.
Common situations: Builder 'save' (runSave) submitting resume JSON whose structure no longer matches what the backend expects after an API schema change; saving a resume deleted in another tab; oversized payload or invalid field types (e.g. null where array expected) rejected with 422.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- ${data.detail || Failed to generate enhancements (status ${r
- Failed to load resume (status ${res.status}).
- Failed to load resumes list (status ${res.status}).
- Failed to download resume (status ${res.status}): ${text}
- Failed to delete resume (status ${res.status}): ${text}
AI-assisted analysis of srbhr/Resume-Matcher@116f9cc3b0 (2026-08-28).
Data as JSON: /api/errors/8d1674d87115cc3d.
Report an issue: GitHub.