PaddlePaddle/PaddleOCR · warning · InvalidRequestError

Destination parent must be a directory: {parent}

Error message

Destination parent must be a directory: {parent}

What it means

InvalidRequestError raised by _resolve_destination when target.parent exists but is not a directory — i.e. a regular file (or other non-directory entry) occupies the path where a directory is required. Distinct from 194 (parent missing) and 197 (destination itself not a dir in the batch helpers).

Source

Thrown at paddleocr/_api_client/_resources.py:138


def _resolve_destination(
    url_path: str, destination: str, filename: Optional[str]
) -> Path:
    destination_path = Path(destination)
    if filename is not None:
        _validate_result_resource_filename(filename)
        target = destination_path / filename
    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}")

View on GitHub (pinned to 2661c7c0ef)

Solutions

  1. Inspect the computed target.parent with ls to find the offending file and remove/rename it or fix the path construction
  2. Do not pass a file path as destination when also supplying filename — destination should be the directory in that case
  3. Guard at the call site: assert Path(dest).is_dir() before calling when destination is expected to be a directory

Example fix

# before
save_resource(url, 'out.zip', filename='img.png')  # out.zip is a file -> parent not a dir

# after
save_resource(url, 'out_dir', filename='img.png')  # destination is a directory
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
target = Path(destination) / (filename or 'x')
assert not target.parent.exists() or target.parent.is_dir(), f'{target.parent} is a file'

Try / catch

try:
    save_resource(url, dest, filename=filename)
except InvalidRequestError as e:
    if 'must be a directory' in str(e):
        raise ValueError(f'destination layout wrong: {e}') from e
    raise

Prevention

When it happens

Trigger: save_resource(url, 'somewhere/file.txt/img.png') where 'somewhere/file.txt' is an existing file; or destination names an existing file and filename is appended beneath it; or a symlink at the parent path points to a file.

Common situations: Destination template bug producing path/to/file.png/extra; destination argument that already includes the filename and a filename kwarg also supplied; leftover file created at the path where a directory was intended.

Related errors


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