Native Library Compatibility#
Docling depends on three families of compiled (native) libraries, each with distinct compatibility concerns:
| Library | Role | Key risks |
|---|---|---|
| docling-parse (C++) | PDF parsing, text/cell extraction | Native memory accumulates across pages; Python GC cannot reclaim it |
| pypdfium2 (libpdfium binding) | PDF rendering, image extraction, fallback text extraction | Thread safety via global lock; native buffer lifecycle must be managed explicitly |
| ONNX Runtime | Neural inference (layout, classification, OCR) | Version ceiling enforced per platform; PP-OCRv6 only supported on ONNX backend, not torch |
Platform detection governs which backends are selected at runtime. The environment is probed via sys.platform and platform.machine() in auto_ocr_model.py and auto_inline_engine.py, and platform metadata is stamped into every output document .
docling-parse: C++ Memory Accumulation#
The DoclingParseDocumentBackend (and its dlparse_v4 variant) hold internal C++ state that grows page-by-page across an entire conversion. Python's garbage collector cannot reclaim this memory. On large or image-heavy PDFs, this reliably triggers std::bad_alloc — typically around page 80 on an 80 MB, 184-page scanned document . The issue is tracked upstream in docling-parse#227 and docling#3345.
Three backends, three failure modes on the same large PDF :
| Backend | Failure mode |
|---|---|
docling_parse (default) | std::bad_alloc crash — loud, visible |
dlparse_v4 | Same crash as docling_parse |
threaded_docling_parse | Silently exits with zero output — no exception, no file written |
pypdfium2 | Works; lower table cell quality (fragmented sub-word cells) |
The threaded_docling_parse backend has a release_native_memory_every_n_pages option (default: 128 pages) configured via ThreadedDoclingParseBackendOptions. ⚠️ Two caveats:
--release-native-memory-every-n-pagesis silently ignored when used with any backend other thanthreaded_docling_parse.threaded_docling_parsehas a confirmed bug that silently drops tables (docling#3512) — do not use it in production where table accuracy matters.
Mitigations:
- CLI:
--pdf-backend pypdfium2— simplest workaround for scanned/OCR-dominated documents. - Python API: Chunk by page range, creating a new
DocumentConverterper chunk and callingdel converterafterward to force the C++ backend to release memory .
pypdfium2: Thread Safety and Native Buffer Management#
pypdfium2 wraps libpdfium, a C library. All native operations are serialized through a global pypdfium2_lock (a threading.Lock) defined in locks.py. This lock is held across 15+ call sites in pypdfium2_backend.py: page geometry access, text cell computation, bitmap enumeration, image rendering, and page/document lifecycle.
Native buffer lifecycle: The get_page_image() method uses a copy-then-close pattern to avoid dangling references to native memory :
- Render the page into a native bitmap.
- Call
bitmap.to_pil().copy()— copies pixel data into Python-managed memory. - Call
bitmap.close()— releases the native buffer. - Release
pypdfium2_lock, then resize the PIL image.
This ensures that the PIL image returned to Python never shares memory with the native bitmap.
API version compatibility: get_bitmap_rects() branches on the pypdfium2 version — obj.get_bounds() for v5.x+, obj.get_pos() for v4.x .
Termux / Android: On Android aarch64 (Termux), the linker emits warnings about libpdfium.so containing unknown processor-specific DT entries . These are non-fatal; pypdfium2 itself functions correctly on that platform.
ONNX Runtime: Version Constraints and PP-OCRv6 Incompatibility#
ONNX Runtime is used in three inference engine contexts: object detection (onnxruntime_engine.py), image classification (onnxruntime_engine.py), and RapidOCR .
Version ceilings (from pyproject.toml):
| Context | Constraint |
|---|---|
RapidOCR (feat-ocr-rapidocr-onnx) | onnxruntime>=1.7.0,<2.0.0 — Python < 3.14 only |
Model inference (models-onnxruntime) | onnxruntime<1.24 on macOS; onnxruntime-gpu<1.24 on Linux/Windows — Python < 3.14 only |
ONNX Runtime is not installed on Python 3.14+ due to these constraints.
PP-OCRv6 incompatibility: RapidOCR 3.9+ defaults to PP-OCRv6 model weights, but the torch inference backend does not support PP-OCRv6 — only the ONNX backend does . This produces:
ValueError: Unsupported configuration: torch.PP-OCRv6.det.small
The default RapidOcrOptions.backend is "onnxruntime" in code , so this error only appears when users or library code explicitly select the torch backend while running RapidOCR 3.9+.
Fixes :
- Switch to ONNX backend:
RapidOcrOptions(backend="onnxruntime")+pip install onnxruntime - Downgrade:
pip install rapidocr==3.8.4(intermediate workaround until upstream resolves this)
Platform-Specific Behavior#
OCR engine auto-selection :
| Platform | Selected engine |
|---|---|
| macOS | OcrMacModel (Apple Vision framework) |
| Linux | NemotronOcrModel (if requirements met; see below) |
| All platforms (fallback chain) | RapidOCR (onnxruntime) → EasyOCR → RapidOCR (torch) |
Nemotron OCR is the most constrained native backend :
- Linux x86_64 only
- Python 3.12 exactly
- CUDA 13.x required
- Install:
pip install "docling[feat-ocr-nemotron]"
The pyproject.toml dependency marker enforces this: python_version == "3.12" and sys_platform == "linux" and platform_machine == "x86_64" .
VLM inference engine selection : Darwin + MPS device → MLX engine; CUDA + prefer_vllm → vLLM; otherwise Transformers. The MLX engine serializes on a process-wide global lock because MLX is not thread-safe .
Termux / Android (non-standard platform): torchao 0.17.0+cpu causes a Bus error crash on Android aarch64 with kernel 4.14 and Python 3.14 — the native module csrc_meta_ops fails to load . torchao is not a Docling dependency and must have been installed externally. Fix: pip uninstall torchao. The root cause is a compiled binary incompatible with the older kernel, unrelated to Docling's own libraries.
Key Source Files#
| File | Purpose |
|---|---|
docling/backend/pypdfium2_backend.py | pypdfium2 document/page backend; lock usage, bitmap copy-then-close pattern |
docling/utils/locks.py | Global pypdfium2_lock definition |
docling/backend/docling_parse_backend.py | DoclingParseDocumentBackend, ThreadedDoclingParseDocumentBackend; native memory release |
docling/datamodel/backend_options.py | ThreadedDoclingParseBackendOptions incl. release_native_memory_every_n_pages |
docling/models/inference_engines/object_detection/onnxruntime_engine.py | ONNX inference session for object detection |
docling/models/inference_engines/image_classification/onnxruntime_engine.py | ONNX inference session for image classification |
docling/models/stages/ocr/auto_ocr_model.py | OCR engine auto-selection; platform branching logic |
docling/models/stages/ocr/nemotron_ocr_model.py | Nemotron OCR runtime validation (platform/arch/Python/CUDA checks) |
docling/models/inference_engines/vlm/auto_inline_engine.py | VLM engine selection by platform/device |
pyproject.toml | All platform-conditional dependency markers for onnxruntime, rapidocr, nemotron, MLX |