srbhr/Resume-Matcher · error · HTTPException

Could not extract text from the uploaded file. The document

Error message

Could not extract text from the uploaded file. The document may be image-based or scanned. Please upload a text-based PDF/DOCX with selectable text, or run OCR first.

What it means

upload_resume raises this HTTP 422 after document parsing succeeded but produced no extractable text. It specifically guards against image-based or scanned PDFs where markdown_content is empty or whitespace-only, since downstream analysis requires selectable text.

Source

Thrown at apps/backend/app/routers/resumes.py:667

            detail=f"File too large. Maximum size: {MAX_FILE_SIZE // (1024 * 1024)}MB",
        )

    if len(content) == 0:
        raise HTTPException(status_code=400, detail="Empty file")

    # Convert to markdown
    try:
        markdown_content = await parse_document(content, file.filename or "resume.pdf")
    except Exception as e:
        logger.error(f"Document parsing failed: {e}")
        raise HTTPException(
            status_code=422,
            detail="Failed to parse document. Please ensure it's a valid PDF or DOCX file.",
        )

    # Validate extracted text is not empty (image-based PDFs / scanned documents)
    if not markdown_content or not markdown_content.strip():
        raise HTTPException(
            status_code=422,
            detail=(
                "Could not extract text from the uploaded file. The document may be "
                "image-based or scanned. Please upload a text-based PDF/DOCX with "
                "selectable text, or run OCR first."
            ),
        )

    # Store in database first with "processing" status (atomic master assignment)
    # original_markdown is preserved permanently for date reference even after
    # builder saves overwrite `content` with JSON.
    resume = await db.create_resume_atomic_master(
        content=markdown_content,
        content_type="md",
        filename=file.filename,
        processed_data=None,
        processing_status="processing",
        original_markdown=markdown_content,

View on GitHub (pinned to 116f9cc3b0)

Solutions

  1. Run OCR on the document (e.g. ocrmypdf input.pdf output.pdf) and re-upload the OCR'd file
  2. Re-export the resume from its source application as a text-based PDF (Save As PDF, not print-to-image)
  3. Upload a DOCX version instead, which almost always contains selectable text
  4. Verify locally that text extraction works before uploading (pdftotext file.pdf returns non-empty output)

Example fix

// before: raw scanned PDF from flatbed scanner
upload_resume(file=open('scan.pdf','rb'))  # 422 image-based
// after
subprocess.run(['ocrmypdf','scan.pdf','scan-ocr.pdf'])
upload_resume(file=open('scan-ocr.pdf','rb'))
Defensive patterns

Strategy: validation

Validate before calling

async function hasSelectableText(file: File): Promise<boolean> {
  if (file.type === 'application/pdf') {
    const buf = await file.arrayBuffer();
    const pdf = await pdfjsLib.getDocument({data: buf}).promise;
    for (let i = 1; i <= Math.min(pdf.numPages, 3); i++) {
      const tc = await (await pdf.getPage(i)).getTextContent();
      if (tc.items.some(it => it.str.trim())) return true;
    }
    return false;
  }
  return true; // DOCX is text-based
}
if (!await hasSelectableText(file)) alert('Run OCR before uploading');

Type guard

function isTextBased(doc: {extractedText?: string | null}): doc is {extractedText: string} {
  return typeof doc.extractedText === 'string' && doc.extractedText.trim().length > 0;
}

Try / catch

try {
  await api.uploadResume(file);
} catch (e) {
  if (e.response?.status === 422 && /image-based|OCR/.test(e.response.data?.detail ?? '')) {
    showOcrInstructions(); // e.g. link to ocrmypdf / online OCR
  } else throw e;
}

Prevention

When it happens

Trigger: POSTing a resume upload (POST to the resumes upload endpoint) with a PDF or DOCX whose pages are raster images (scanned document, photographed page, screenshot-exported PDF), so the parser extracts an empty/whitespace-only markdown_content.

Common situations: Users scan paper resumes into PDFs; resume exported from design tools as flattened images; faxed or photographed documents; PDF with embedded fonts the extractor cannot decode; OCR preprocessing was skipped.

Related errors


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