srbhr/Resume-Matcher · error · Error

Upload failed with status

Error message

Upload failed with status ${res.status}

What it means

uploadJobDescriptions in apps/frontend/lib/api/resume.ts throws this terse Error when the POST to `/jobs/upload` returns a non-OK status, including only the numeric status code (no response body). It indicates the backend rejected the batch of job descriptions or failed while creating the job record; diagnosing requires inspecting the network tab or backend logs since the message omits details.

Solutions

  1. Reproduce in the browser network tab to read the response body, since this message omits it; classify 401/404/413/422/5xx.
  2. Validate before calling: descriptions is a non-empty array of non-empty strings and resumeId is a non-empty ID.
  3. For 413, chunk the upload into smaller batches or raise the backend body-size limit.
  4. For 401, re-authenticate and retry; for 404, verify the resume exists.
  5. For 5xx, check backend logs for job-creation/storage errors.

Example fix

// before
const res = await apiPost('/jobs/upload', {
  job_descriptions: descriptions,
  resume_id: resumeId,
});
if (!res.ok) throw new Error(`Upload failed with status ${res.status}`);
// after
if (!Array.isArray(descriptions) || descriptions.length === 0) {
  throw new Error('Provide at least one job description to upload.');
}
const res = await apiPost('/jobs/upload', {
  job_descriptions: descriptions,
  resume_id: resumeId,
});
if (!res.ok) {
  const data = await res.json().catch(() => ({}));
  throw new Error(data.detail || `Upload failed with status ${res.status}`);
}
Defensive patterns

Strategy: validation

Validate before calling

const cleaned = (descriptions ?? []).map(d => (d ?? '').trim()).filter(Boolean);
if (cleaned.length === 0) throw new Error('Provide at least one non-empty job description.');
if (!resumeId?.trim()) throw new Error('resumeId is required to upload job descriptions.');

Type guard

function hasUploadableDescriptions(v: unknown): v is string[] {
  return Array.isArray(v) && v.length > 0 &&
    v.every(d => typeof d === 'string' && d.trim().length > 0);
}

Try / catch

try {
  const jobId = await uploadJobDescriptions(descriptions, resumeId);
} catch (e) {
  const msg = e instanceof Error ? e.message : '';
  if (msg.includes('status 413')) showError('Too much content — upload fewer or shorter descriptions.');
  else if (msg.includes('status 401')) redirectToLogin();
  else showError('Upload failed. Please retry.');
}

Prevention

When it happens

Trigger: apiPost('/jobs/upload', { job_descriptions: descriptions, resume_id: resumeId }) resolves with res.ok === false: 401 unauthenticated, 404 resume_id not found, 422 empty descriptions array or items exceeding size/count limits, 413 payload too large, or 5xx backend storage/processing failure.

Common situations: Pasting very large or many job descriptions exceeds the request body limit (413); descriptions array is empty because scraping/parsing yielded nothing (422); resumeId references a deleted resume (404); auth cookie expired (401).

Related errors


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

Appendix: source

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

  try {
    return JSON.parse(text) as ImprovedResult;
  } catch (parseError) {
    console.error('Failed to parse improve response:', parseError, 'Raw response:', text);
    throw parseError;
  }
}

/** Uploads job descriptions and returns a job_id */
export async function uploadJobDescriptions(
  descriptions: string[],
  resumeId: string
): Promise<string> {
  const res = await apiPost('/jobs/upload', {
    job_descriptions: descriptions,
    resume_id: resumeId,
  });
  if (!res.ok) throw new Error(`Upload failed with status ${res.status}`);
  const data = await res.json();
  return data.job_id[0];
}

/** Improves the resume and returns the full preview object */
export async function improveResume(
  resumeId: string,
  jobId: string,
  promptId?: string
): Promise<ImprovedResult> {
  return postImprove('/resumes/improve', {
    resume_id: resumeId,
    job_id: jobId,
    prompt_id: promptId ?? null,
  });
}

/** Previews the resume improvement without saving */

View on GitHub (pinned to 116f9cc3b0)