PaddlePaddle/PaddleOCR · warning · InvalidRequestError

Destination must be an existing directory: {destination}

Error message

Destination must be an existing directory: {destination}

What it means

InvalidRequestError raised by _require_existing_directory when the destination exists but is not a directory (a regular file, broken behavior for the batch helpers). The batch save functions iterate pages and write multiple files, so destination must be a directory that already exists.

Source

Thrown at paddleocr/_api_client/_resources.py:147

    elif destination_path.exists() and destination_path.is_dir():
        target = destination_path / _safe_url_basename(url_path)
    else:
        target = destination_path

    parent = target.parent
    if not parent.exists():
        raise FileNotFoundError(str(parent))
    if not parent.is_dir():
        raise InvalidRequestError(f"Destination parent must be a directory: {parent}")
    return target


def _require_existing_directory(destination: str) -> Path:
    dest_dir = Path(destination)
    if not dest_dir.exists():
        raise FileNotFoundError(destination)
    if not dest_dir.is_dir():
        raise InvalidRequestError(
            f"Destination must be an existing directory: {destination}"
        )
    return dest_dir


def _require_writable_target(target: Path, overwrite: bool) -> None:
    if target.exists() and not overwrite:
        raise InvalidRequestError(f"Destination already exists: {target}")


def _atomic_write(target: Path, content: bytes, overwrite: bool) -> None:
    fd, temp_path = tempfile.mkstemp(
        prefix=f".{target.name}.tmp-",
        dir=str(target.parent),
    )
    try:
        with os.fdopen(fd, "wb") as temp_file:
            temp_file.write(content)

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Pass the directory, not a file path, to the batch helpers
  2. If a file occupies the name, move/remove it and create a real directory there
  3. Use a consistent convention: directories end with the output dir; individual filenames are chosen by the helper (ocr-page-N.png, markdown image names)

Example fix

# before
save_ocr_result_resources(result, '/data/out/pages.zip')  # existing file

# after
save_ocr_result_resources(result, '/data/out/pages')  # existing directory
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
p = Path(destination)
if p.exists() and not p.is_dir():
    raise ValueError(f'{destination} is a file; pass a directory')
p.mkdir(parents=True, exist_ok=True)

Type guard

def is_directory(p: str) -> bool:
    from pathlib import Path
    return Path(p).is_dir()

Try / catch

try:
    save_ocr_result_resources(result, dest)
except InvalidRequestError as e:
    if 'existing directory' in str(e):
        raise ValueError(f'pass the output directory, not a file: {e}') from e
    raise

Prevention

When it happens

Trigger: save_ocr_result_resources(result, 'results.tar') where results.tar is a file; destination points at a symlink to a file; a previously-created output placeholder file shares the intended directory name.

Common situations: Caller passes a full file path (copied from a save_resource call) instead of the directory; a file was accidentally created where the output directory should be; config value mixes up dir and file semantics.

Related errors


AI-assisted analysis of PaddlePaddle/PaddleOCR@2661c7c0ef (2026-08-14). Data as JSON: /api/errors/5406bbf6a21aba4b. Report an issue: GitHub.