opendataloader-project/opendataloader-pdf · error · ValueError

OCR engine ' ' is not supported in hybrid local mode…

Error message

OCR engine '{ocr_engine}' is not supported in hybrid local mode (filtered by _OCR_ENGINE_DENYLIST). Available engines: {available}

What it means

ValueError raised when the requested ocr_engine is in the module-level _OCR_ENGINE_DENYLIST (currently frozenset({'kserve_v2_ocr'})), which blocks remote-inference-server OCR engines in hybrid local mode for security and reproducibility. This guard is enforced in create_converter too, not just the CLI argparse choices, because programmatic callers bypass argparse. The message lists the engines that remain available (registered kinds minus the denylist).

Solutions

  1. Switch to an allowed engine: use one of the values printed in the 'Available engines' list (e.g. easyocr, tesseract, or auto).
  2. If you genuinely need remote inference, do NOT use hybrid local mode — run the engine as a separate service and adjust your architecture, since the denylist is intentional for local mode.
  3. Update any persisted config/defaults that reference a denied engine name.
  4. Check the current denylist via from opendataloader_pdf.hybrid_server import _OCR_ENGINE_DENYLIST before choosing an engine programmatically.

Example fix

# before: denied remote-inference engine in local mode
create_converter(ocr_engine='kserve_v2_ocr')  # -> ValueError
# after: use a locally-available engine
create_converter(ocr_engine='easyocr')
Defensive patterns

Strategy: validation

Validate before calling

from opendataloader_pdf.hybrid_server import _OCR_ENGINE_DENYLIST
def is_allowed_engine(engine: str) -> bool:
    return engine not in _OCR_ENGINE_DENYLIST

Type guard

def is_denied_engine_error(exc: ValueError) -> bool:
    return isinstance(exc, ValueError) and "_OCR_ENGINE_DENYLIST" in str(exc)

Try / catch

try:
    create_converter(ocr_engine=engine)
except ValueError as e:
    if "_OCR_ENGINE_DENYLIST" in str(e):
        # pick a locally-available engine instead
        engine = "easyocr"
        create_converter(ocr_engine=engine)
    raise

Prevention

When it happens

Trigger: Calling create_converter (or the hybrid server path) with ocr_engine='kserve_v2_ocr' (or any future value added to _OCR_ENGINE_DENYLIST). Programmatic import-and-call bypasses the CLI's argparse choices validation, so this in-function check is what catches it.

Common situations: A caller copies an engine name from docling docs that includes a remote inference server engine blocked here. A config defaults to a denied engine. An upgrade added a new engine to the denylist that the caller's saved config still references.

Related errors


AI-assisted analysis of opendataloader-project/opendataloader-pdf@a7789b8e77 (2026-08-14). Data as JSON: /api/errors/e9387cfe330bec2a. Report an issue: GitHub.

Appendix: source

Thrown at python/opendataloader-pdf/src/opendataloader_pdf/hybrid_server.py:449

        TableFormerMode,
        TableStructureOptions,
        TesseractCliOcrOptions,
        TesseractOcrOptions,
    )
    from docling.document_converter import DocumentConverter, PdfFormatOption
    from docling.models.factories import get_ocr_factory

    # Delegate engine selection to docling's factory. We block external plugins for
    # security/reproducibility; the module-level _OCR_ENGINE_DENYLIST filters
    # engines unsuitable for hybrid local mode (e.g., remote inference servers).
    ocr_factory = get_ocr_factory(allow_external_plugins=False)
    if ocr_engine in _OCR_ENGINE_DENYLIST:
        # Programmatic callers (importing this module) bypass argparse `choices`,
        # so enforce the denylist here too. Without this, the module-level claim
        # that `_OCR_ENGINE_DENYLIST` is shared across CLI and create_converter
        # would only be true at the CLI layer.
        available = sorted(set(ocr_factory.registered_kind) - _OCR_ENGINE_DENYLIST)
        raise ValueError(
            f"OCR engine '{ocr_engine}' is not supported in hybrid local mode "
            f"(filtered by _OCR_ENGINE_DENYLIST). Available engines: {available}"
        )
    try:
        ocr_options = ocr_factory.create_options(
            kind=ocr_engine,
            force_full_page_ocr=force_full_page_ocr,
        )
    except RuntimeError as e:
        # Library-friendly error type so programmatic callers can catch and retry
        # with a different engine. main() relies on argparse `choices` to gate
        # invalid CLI input, so this branch is reached only via direct calls.
        available = sorted(set(ocr_factory.registered_kind) - _OCR_ENGINE_DENYLIST)
        raise ValueError(
            f"Unknown ocr_engine '{ocr_engine}': {e}\nAvailable engines: {available}"
        ) from e

    if ocr_lang:

View on GitHub (pinned to a7789b8e77)