pdftract/notes/bf-4482v.md
jedarden 6117735ed2 docs(bf-4482v): document extraction result error structure
- Analyzed ExtractionResult and ExtractionMetadata structure
- Documented internal Diagnostic structure with code, offset, object_ref, message
- Mapped DiagCode categories (STRUCT_*, STREAM_*, XREF_*, etc.)
- Explained severity levels (Info, Warning, Error, Fatal)
- Traced error flow from emission to string conversion
- Identified key files: extract.rs, diagnostics.rs

Closes bf-4482v
2026-07-05 19:31:29 -04:00

5.2 KiB

Extraction Result Error Structure Analysis

Overview

This document analyzes how errors and diagnostics are organized in the pdftract extraction result structure.

ExtractionResult Structure

The main ExtractionResult struct (defined in crates/pdftract-core/src/extract.rs) contains:

pub struct ExtractionResult {
    pub fingerprint: String,
    pub pages: Vec<PageResult>,
    pub metadata: ExtractionMetadata,
    pub signatures: Vec<SignatureJson>,
    pub form_fields: Vec<FormFieldJson>,
    pub links: Vec<LinkJson>,
    pub attachments: Vec<AttachmentJson>,
    pub threads: Vec<ThreadJson>,
    pub javascript_actions: Vec<JavascriptActionJson>,
}

Error Reporting Locations

1. Document-Level Diagnostics

Location: ExtractionResult.metadata.diagnostics: Vec<String>

Purpose: Contains warnings and errors that affect the entire extraction process.

Examples:

  • Tagged PDF detected (StructTree traversal deferred)
  • Xref repaired via forward scan
  • Coverage warnings
  • Page range errors

2. Per-Page Errors

Location: PageResult.error: Option<String>

Purpose: Contains error messages when extraction fails for a specific page.

Structure:

pub struct PageResult {
    pub index: usize,
    pub page_number: u32,
    pub page_label: Option<String>,
    pub width: Option<f32>,
    pub height: Option<f32>,
    pub rotation: Option<u16>,
    pub page_type: Option<String>,
    pub spans: Vec<SpanJson>,
    pub blocks: Vec<BlockJson>,
    pub tables: Vec<TableJson>,
    pub annotations: Vec<AnnotationJson>,
    pub error: Option<String>,  // ← Per-page error
}

3. Metadata Error Count

Location: ExtractionMetadata.error_count: usize

Purpose: Tracks the total number of pages that failed to extract.

Internal Diagnostic Structure

Before being converted to strings, diagnostics use the rich Diagnostic structure:

pub struct Diagnostic {
    pub code: DiagCode,                    // Error type code
    pub byte_offset: Option<u64>,          // Location in file
    pub object_ref: Option<ObjRef>,         // PDF object reference
    pub message: Cow<'static, str>,        // Human-readable message
}

Diagnostic Code Categories

The DiagCode enum categorizes errors with prefixes:

Category Prefix Description
Structure STRUCT_* PDF structure errors (parser/object/document)
Stream STREAM_* Stream decoder errors
XREF XREF_* Cross-reference table errors
Encryption ENCRYPTION_* Encryption-related errors
Page PAGE_* Page-level errors
Font FONT_* Font pipeline errors
Graphics State GSTATE_* Graphics state errors
Layout LAYOUT_* Layout and reading order errors
OCR OCR_* OCR pipeline errors
Remote REMOTE_* Remote source errors
MCP MCP_* MCP server errors
Cache CACHE_* Cache errors
Security SECURITY_* Security-related diagnostics

Severity Levels

Diagnostics have four severity levels:

  1. Info — Does not affect output validity

    • Examples: XREF_REPAIRED, TAGGED_PDF_STRUCT_TREE_DEFERRED
  2. Warning — Output is usable but degraded

    • Examples: STRUCT_INVALID_NAME, FONT_GLYPH_UNMAPPED, STREAM_DECODE_ERROR
  3. Error — Output for this region/page is invalid; other regions OK

    • Examples: STREAM_BOMB, PAGE_OUT_OF_RANGE, REMOTE_FETCH_INTERRUPTED
  4. Fatal — Extraction aborted, no usable output

    • Examples: ENCRYPTION_UNSUPPORTED, REMOTE_TLS_FAILED, REMOTE_DNS_FAILED

Error Flow

  1. Emission: During extraction, Diagnostic structs are emitted via the emit!() macro
  2. Collection: Diagnostics are collected in Vec<Diagnostic>
  3. Conversion: Before returning ExtractionResult, diagnostics are converted to strings:
    let diagnostics: Vec<String> = coverage_result
        .diagnostics
        .iter()
        .map(|d| d.message.as_ref().to_string())
        .collect();
    
  4. Exposure: Final string messages are exposed in ExtractionMetadata.diagnostics

Key Files

  • crates/pdftract-core/src/extract.rs - Main extraction pipeline, defines ExtractionResult
  • crates/pdftract-core/src/diagnostics.rs - Diagnostic system, defines Diagnostic and DiagCode
  • src/graphics_state/diagnostics.rs - Legacy diagnostic types (graphics state only)

Example Diagnostic

// Creating a diagnostic
Diagnostic::with_static(
    DiagCode::StreamDecodeError,
    12345,  // byte offset
    "zlib stream truncated mid-inflation"
)

// Converts to string in metadata:
// "STREAM_DECODE_ERROR: zlib stream truncated mid-inflation (byte offset 12345)"

Acceptance Criteria Status

Error array structure understood

  • Document-level diagnostics in metadata.diagnostics
  • Per-page errors in PageResult.error

Error fields documented

  • Internal Diagnostic struct with code, offset, object_ref, message
  • Final string representation in extraction result

Relationship between errors and extraction phases clear

  • Each diagnostic has a phase origin (e.g., "1.1", "1.5", "3.1")
  • DiagCode category prefixes indicate which layer emitted the error
  • Severity levels indicate impact on extraction result