ocrmypdf/OCRmyPDF · error · BadArgsError

--sidecar filename needed when output file is not a path.

Error message

--sidecar filename needed when output file is not a path.

What it means

Defensive check: the '\0' sidecar sentinel is only produced by the CLI, which always supplies output_file as a path. If output_file is not a str/Path (e.g. a file stream passed via the Python API), the caller mixed the CLI sentinel with the stream API.

Solutions

  1. Pass an explicit sidecar path string when using the API
  2. Or omit sidecar and let it default (only auto-derivation from a real path is supported)

Example fix

# before
ocrmypdf.ocr(BytesIO(pdf_bytes), BytesIO(), sidecar='\0')
# after
sidecar = io.StringIO()
ocrmypdf.ocr(BytesIO(pdf_bytes), BytesIO(), sidecar=sidecar)
Defensive patterns

Strategy: type-guard

Type guard

def is_valid_output(v) -> bool:
    return isinstance(v, (str, Path)) or v in ('-', os.devnull)

Prevention

When it happens

Trigger: Calling the Python API with sidecar='\0' (a CLI-internal sentinel) while passing a stream object as output_file.

Common situations: API users copying CLI sentinel behavior; wrapping ocrmypdf in a service that writes to BytesIO.

Related errors


AI-assisted analysis of ocrmypdf/OCRmyPDF@5074a0b0e1 (2026-08-27). Data as JSON: /api/errors/4e79da480c08f5b7. Report an issue: GitHub.

Appendix: source

Thrown at src/ocrmypdf/_validation.py:102

            "\n"
        )
        raise MissingDependencyError(msg)


def check_options_sidecar(options: OcrOptions) -> None:
    if options.sidecar == '\0':
        if options.output_file == '-':
            raise BadArgsError("--sidecar filename needed when output file is stdout.")
        elif options.output_file == os.devnull:
            raise BadArgsError(
                "--sidecar filename needed when output file is /dev/null or NUL."
            )
        elif not isinstance(options.output_file, str | Path):
            # The '\0' sentinel is only ever set by the CLI, which always
            # supplies output_file as a plain path - not a stream. If this
            # somehow fires, the caller mixed a CLI-only sentinel with the
            # stream-based API.
            raise BadArgsError(
                "--sidecar filename needed when output file is not a path."
            )
        options.sidecar = os.fspath(options.output_file) + '.txt'
    if options.sidecar == options.input_file or options.sidecar == options.output_file:
        raise BadArgsError(
            "--sidecar file must be different from the input and output files"
        )


def check_options_preprocessing(options: OcrOptions) -> None:
    if options.clean_final:
        options.clean = True
    if options.unpaper_args and not options.clean:
        raise BadArgsError("--clean is required for --unpaper-args")
    if (
        options.rotate_pages_threshold != DEFAULT_ROTATE_PAGES_THRESHOLD
        and not options.rotate_pages
    ):

View on GitHub (pinned to 5074a0b0e1)