srbhr/Resume-Matcher · error · HTTPException

Failed to parse document. Please ensure it's a valid PDF or

Error message

Failed to parse document. Please ensure it's a valid PDF or DOCX file.

What it means

When parse_document (markitdown-based PDF/DOC → Markdown conversion) raises for any reason, upload_resume logs the real exception server-side and re-raises HTTPException 422 with this generic, action-oriented detail. It is the standard backend pattern: log details, return a generic client message.

Source

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

        )

    # Read and validate size
    content = await file.read()
    if len(content) > MAX_FILE_SIZE:
        raise HTTPException(
            status_code=413,
            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.

View on GitHub (pinned to 116f9cc3b0)

Solutions

  1. Check the backend log 'Document parsing failed: <e>' for the real cause
  2. Open the file locally; if it prompts for a password, remove protection before uploading
  3. Re-export the document as a clean PDF or DOCX from the original application
  4. Verify markitdown and its PDF extras are installed at pinned versions (uv sync)

Example fix

// before: uploading a password-protected PDF
POST /api/v1/resumes/upload file=protected.pdf  -> 422
// after: strip protection first, then upload
qpdf --decrypt protected.pdf resume.pdf && curl -F 'file=@resume.pdf;type=application/pdf' http://localhost:8000/api/v1/resumes/upload
Defensive patterns

Strategy: validation

Validate before calling

// client: sanity-check the file opens as a PDF before upload
const buf = new Uint8Array(await file.slice(0, 5).arrayBuffer());
const isPdf = String.fromCharCode(...buf) === '%PDF-';
if (file.name.endsWith('.pdf') && !isPdf) {
  throw new Error('File is not a real PDF (missing %PDF- header) — re-export it');
}

Try / catch

try {
  await api.postForm('/resumes/upload', form);
} catch (e) {
  if (e.response?.status === 422 && /Failed to parse document/.test(e.response?.data?.detail ?? '')) {
    showToast('Could not read that document — remove password protection and re-export as PDF/DOCX');
  }
}

Prevention

When it happens

Trigger: POST /api/v1/resumes/upload with a file that passes type/size checks but cannot be converted: corrupt or password-protected PDFs, DRM-protected files, malformed DOCX (renamed .txt or .html), or a markitdown/dependency failure on an exotic PDF structure.

Common situations: Password-protected PDFs; files renamed to .pdf but actually images/HTML; encrypted PDFs from secure portals; markitdown version issues after a dependency upgrade; corrupt uploads from flaky networks.

Understand the failure class

Related errors


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