docling-project/docling · error · ValueError

Invalid filename: contains null byte.

Error message

Invalid filename: contains null byte.

What it means

Filenames handed to the Tesseract CLI (e.g. input images and output bases) are sanitized before subprocess invocation. A filename containing a NUL byte raises this ValueError, mirroring the path and command sanitizers, since NUL bytes cannot occur in valid filenames and signal malformed or injected input.

Solutions

  1. Clean or drop the offending path; strip control characters from file lists before processing.
  2. Validate external path sources (manifests, uploads) with a null-byte check up front.
  3. Log repr(path) for failed batches to identify the corrupted entry quickly.

Example fix

# before
paths = ["page1.png\x00", "page2.png"]

# after
paths = [p for p in raw_paths if "\x00" not in p]
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def safe_filenames(paths: list[str]) -> list[str]:
    bad = [p for p in paths if "\x00" in p]
    if bad:
        raise ValueError(f"filenames contain NUL bytes: {bad!r}")
    return [str(Path(p).resolve()) for p in paths]

Type guard

def is_nul_free_filename(p: str) -> bool:
    return bool(p) and "\x00" not in p

Try / catch

try:
    model.run_batch(files)
except ValueError as e:
    if "Invalid filename" in str(e):
        files = [f for f in files if is_nul_free_filename(f)]
        model.run_batch(files)
    else:
        raise

Prevention

When it happens

Trigger: Any flow that passes a filename containing \x00 to the CLI-based Tesseract model — e.g. processing a list of files where one path came from binary or corrupted metadata.

Common situations: Batch processing paths read from untrusted manifests, archives with malformed entry names, or strings decoded from binary blobs.

Related errors


AI-assisted analysis of docling-project/docling@61d76f1ff3 (2026-08-14). Data as JSON: /api/errors/0a053c79ababc19b. Report an issue: GitHub.

Appendix: source

Thrown at docling/models/stages/ocr/tesseract_ocr_cli_model.py:132

    @staticmethod
    def _sanitize_cmd(cmd: str) -> str:
        """Validate and sanitize the Tesseract executable name/path to prevent injection.

        Rejects values containing null bytes.
        """
        if "\x00" in cmd:
            raise ValueError("Invalid Tesseract command: contains null byte.")
        return cmd

    @staticmethod
    def _sanitize_filename(filename: str) -> str:
        """Validate and sanitize a filename passed to the Tesseract CLI.

        Rejects paths containing null bytes and resolves to an absolute path.
        """
        if "\x00" in filename:
            raise ValueError("Invalid filename: contains null byte.")
        return str(Path(filename).resolve())

    def _get_name_and_version(self) -> Tuple[str, str]:
        if self._name is not None and self._version is not None:
            return self._name, self._version  # type: ignore

        cmd = [self._safe_tesseract_cmd, "--version"]

        proc = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=False)
        stdout, stderr = proc.communicate()

        proc.wait()

        # HACK: Windows versions of Tesseract output the version to stdout, Linux versions
        # to stderr, so check both.
        version_line = (
            (stdout.decode("utf8").strip() or stderr.decode("utf8").strip())
            .split("\n")[0]

View on GitHub (pinned to 61d76f1ff3)