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

164 lines
5.2 KiB
Markdown

# 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:
```rust
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**:
```rust
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:
```rust
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:
```rust
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
```rust
// 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