How interviewstreet/hiring-agent Handles PDF Extraction Failures: A Code-Level Breakdown

The hiring-agent system prevents partial or invalid resume data by returning None at any failure stage—file reading, text conversion, or section extraction—while logging emoji-prefixed errors for quick diagnosis.

When processing candidate resumes, the interviewstreet/hiring-agent repository must reliably convert PDF documents into structured JSON. Rather than emitting incomplete data, the system's PDFHandler class in pdf.py gracefully handles PDF extraction failures by treating every error as a hard stop, ensuring callers never receive a malformed resume. This article breaks down the exact failure points, source code paths, and logging strategy used to enforce that safety guarantee.

The Three Stages of PDF Extraction Failure Handling

In pdf.py, the PDFHandler class implements a pipeline with explicit failure gates. Each stage returns None on error, preventing downstream processing of corrupt or incomplete data.

Stage 1: PDF File Reading

The extract_text_from_pdf method serves as the first line of defense against filesystem and format errors. According to the interviewstreet/hiring-agent source code, this method catches any exception—such as a missing file or unreadable PDF—logs an error, and returns None instead of raising. This implementation lives in pdf.py between lines 47 and 65, ensuring that unreadable inputs never reach the text-parsing layer.

Stage 2: Text-to-JSON Conversion

The public entry point extract_json_from_pdf calls extract_text_from_pdf and immediately checks the result. If the returned text is None, it logs "❌ Failed to extract text from PDF" and aborts the conversion, returning None to the caller. This guard clause is located in pdf.py at lines 99–108, enforcing a clean pass-or-fail contract at the API boundary.

Stage 3: Section-Wise Extraction

If text extraction succeeds, extract_json_from_pdf proceeds to _extract_all_sections_separately, which iterates over required resume sections such as basics, work, education, skills, projects, and awards. If any individual section extractor returns None, the loop logs a warning ("⚠️ Failed to extract … section. Aborting extraction…") and immediately returns None. This logic, found in pdf.py at lines 89–99, guarantees that a partially filled JSON resume is never emitted.

Logging Strategy for Extraction Failures

All failure points in the PDF pipeline use the standard library logging module with clear, emoji-prefixed messages. This convention makes it easy to spot errors in production logs without parsing stack traces.

  • — Indicates a hard error, such as a missing file or JSON parsing failure.
  • ⚠️ — Indicates a recoverable warning, such as a missing specific resume section.

Because every failure path returns None, calling code can programmatically detect PDF extraction failures and decide whether to retry, fallback to another method, or surface the issue to the user.

Practical Code Examples

Example 1: Safely Extracting a Resume from a PDF

The following pattern shows how to handle a None return from extract_json_from_pdf without crashing your application:

from pdf import PDFHandler

handler = PDFHandler()
json_resume = handler.extract_json_from_pdf("candidate_resume.pdf")

if json_resume is None:
    # Extraction failed – handle gracefully

    print("Could not process the PDF. Please verify the file or try again.")
else:
    # Successful – work with the structured resume

    print(json_resume.model_dump_json())

Example 2: Detecting and Logging the Exact Failure Reason

Enable debug logging to expose the exact stage where PDF extraction fails:

import logging
from pdf import PDFHandler

logging.basicConfig(level=logging.DEBUG)

handler = PDFHandler()
resume = handler.extract_json_from_pdf("/path/to/missing.pdf")

# The logs will contain:

# ❌ Failed to extract text from PDF

# or

# ⚠️ Failed to extract work section. Aborting extraction to prevent partial/invalid resume data.

Example 3: Wrapping the Extraction in a Retry Loop

Because failures are signaled with None, you can wrap the call in a simple retry mechanism:

import logging
import time
from pdf import PDFHandler

handler = PDFHandler()
pdf_path = "resume.pdf"

for attempt in range(3):
    result = handler.extract_json_from_pdf(pdf_path)
    if result:
        break
    logging.warning(f"Attempt {attempt + 1} failed – retrying...")
    time.sleep(2)   # simple back‑off

if result is None:
    raise RuntimeError("All attempts to extract the PDF failed.")

Key Files in the PDF Extraction Pipeline

Several files work together to support end-to-end PDF processing and failure handling in the hiring-agent repository:

  • pdf.py — Core PDF-to-text-to-JSON extraction logic, including all failure handling in PDFHandler.
  • pymupdf_rag.py — Helper that converts MuPDF document pages to Markdown text via to_markdown.
  • llm_utils.py — Wrapper around the LLM provider used for section extraction; also contains JSON-tidying utilities.
  • prompts/template_manager.py — Renders system-message and section-specific prompts fed to the LLM.
  • models.py — Pydantic models (JSONResume, Basics, etc.) that represent the final structured resume.

These files constitute the end-to-end pipeline, with explicit error handling ensuring that any PDF extraction problem results in a clean None return rather than a partially populated resume.

Summary

  • PDFHandler.extract_text_from_pdf catches all file-system and format exceptions and returns None, stopping the pipeline at the source.
  • PDFHandler.extract_json_from_pdf acts as the public entry point, passing None through immediately if text extraction fails.
  • _extract_all_sections_separately enforces all-or-nothing semantics: one failed section aborts the entire JSON build.
  • The logging strategy uses emoji-prefixed messages ( and ⚠️) to distinguish hard errors from section warnings at a glance.
  • Callers should always check for None and implement their own retry or fallback logic when handling PDF extraction failures.

Frequently Asked Questions

What happens when a PDF file is missing or unreadable?

If the file is missing or the PDF is corrupted, extract_text_from_pdf catches the exception and returns None. extract_json_from_pdf then logs "❌ Failed to extract text from PDF" and also returns None, preventing any downstream processing.

Does the system return partial resume data if one section fails?

No. During _extract_all_sections_separately, if any section—such as work, education, or skills—returns None, the method logs a warning and aborts the entire extraction. This design guarantees that callers never receive an incomplete or inconsistent JSON resume.

How can I detect a PDF extraction failure programmatically?

Check the return value of extract_json_from_pdf. A successful extraction returns a Pydantic model instance, while any failure produces None. This explicit pass-fail contract makes it straightforward to implement retries, fallbacks, or user-facing error messages.

Where are the PDF extraction logs configured?

The PDFHandler methods use the Python standard library logging module directly inside pdf.py. There is no separate logging configuration file required; simply set up logging.basicConfig in your entry point to capture the emoji-prefixed error and warning messages emitted by the pipeline.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →