srbhr/Resume-Matcher · error · ValueError

Resume content is empty after text extraction.

Error message

Resume content is empty after text extraction.

What it means

parse_resume_to_json requires non-empty markdown resume text because it will be sent to the LLM for structuring. If markdown_text is None, empty, or whitespace-only after upstream text extraction, it raises this ValueError instead of making a doomed LLM call.

Source

Thrown at apps/backend/app/services/parser.py:158

    finally:
        tmp_path.unlink(missing_ok=True)


async def parse_resume_to_json(markdown_text: str) -> dict[str, Any]:
    """Parse resume markdown to structured JSON using LLM.

    After LLM parsing, patches any year-only dates with month-inclusive
    dates extracted from the raw markdown. This ensures months are never
    lost regardless of LLM behavior.

    Args:
        markdown_text: Resume content in markdown format

    Returns:
        Structured resume data matching ResumeData schema
    """
    if not markdown_text or not markdown_text.strip():
        raise ValueError("Resume content is empty after text extraction.")

    prompt = PARSE_RESUME_PROMPT.format(
        schema=RESUME_SCHEMA_EXAMPLE,
        resume_text=markdown_text,
    )

    config = get_llm_config()
    model_name = get_model_name(config)
    result = await complete_json(
        prompt=prompt,
        system_prompt="You are a JSON extraction engine. Output only valid JSON, no explanations.",
        max_tokens=get_safe_max_tokens(model_name),
        retries=3,
    )

    # Patch dates: restore months the LLM may have dropped
    result = restore_dates_from_markdown(result, markdown_text)

View on GitHub (pinned to 116f9cc3b0)

Solutions

  1. Verify the uploaded file has extractable text (e.g. run pdftotext / inspect extraction output) before parsing
  2. Reject image-only/scanned PDFs at upload and ask the user for a text-based file, or enable OCR preprocessing
  3. Check the file isn't empty/corrupt at upload time and return a 400/422 with a clear message
  4. Log extraction output size to distinguish extractor failures from genuinely empty documents

Example fix

// before
text = extract_text(upload.file)
await parse_resume_to_json(text)  # ValueError if blank
// after
text = extract_text(upload.file)
if not text or not text.strip():
    raise HTTPException(422, "Could not extract text from the uploaded resume (scanned PDF?)")
await parse_resume_to_json(text)
Defensive patterns

Strategy: try-catch

Validate before calling

def extraction_ok(text: str | None) -> bool:
    return bool(text and text.strip())

Type guard

def has_extractable_text(v: object) -> TypeGuard[str]:
    return isinstance(v, str) and v.strip() != ""

Try / catch

try:
    data = await parse_resume_to_json(markdown_text)
except ValueError as e:
    if "empty after text extraction" in str(e):
        raise HTTPException(422, "No readable text in the uploaded resume; try a text-based PDF/DOCX.") from e
    raise

Prevention

When it happens

Trigger: Uploading a resume file whose extraction produced no text (empty file, scanned/image-only PDF with no OCR text, corrupted docx), or calling parse_resume_to_json directly with ""/None; raised in paths reached via upload_resume and retry_processing.

Common situations: User uploads a scanned PDF (image-only, no text layer), a zero-byte file, or an unsupported format that the extractor silently returns empty for; OCR not configured.

Related errors


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