{"record":{"id":"29180c983fb79a5b","repo":"PaddlePaddle/PaddleOCR","slug":"destination-parent-must-be-a-directory-parent-29180c","errorCode":null,"errorMessage":"Destination parent must be a directory: {parent}","messagePattern":"Destination parent must be a directory: (.+?)","errorType":"validation","errorClass":"InvalidRequestError","httpStatus":null,"severity":"warning","filePath":"paddleocr/_api_client/_resources.py","lineNumber":138,"sourceCode":"\n\ndef _resolve_destination(\n    url_path: str, destination: str, filename: Optional[str]\n) -> Path:\n    destination_path = Path(destination)\n    if filename is not None:\n        _validate_result_resource_filename(filename)\n        target = destination_path / filename\n    elif destination_path.exists() and destination_path.is_dir():\n        target = destination_path / _safe_url_basename(url_path)\n    else:\n        target = destination_path\n\n    parent = target.parent\n    if not parent.exists():\n        raise FileNotFoundError(str(parent))\n    if not parent.is_dir():\n        raise InvalidRequestError(f\"Destination parent must be a directory: {parent}\")\n    return target\n\n\ndef _require_existing_directory(destination: str) -> Path:\n    dest_dir = Path(destination)\n    if not dest_dir.exists():\n        raise FileNotFoundError(destination)\n    if not dest_dir.is_dir():\n        raise InvalidRequestError(\n            f\"Destination must be an existing directory: {destination}\"\n        )\n    return dest_dir\n\n\ndef _require_writable_target(target: Path, overwrite: bool) -> None:\n    if target.exists() and not overwrite:\n        raise InvalidRequestError(f\"Destination already exists: {target}\")\n","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/paddleocr/_api_client/_resources.py#L120-L156","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the computed target.parent with ls to find the offending file and remove/rename it or fix the path construction","Do not pass a file path as destination when also supplying filename — destination should be the directory in that case","Guard at the call site: assert Path(dest).is_dir() before calling when destination is expected to be a directory"],"exampleFix":"# before\nsave_resource(url, 'out.zip', filename='img.png')  # out.zip is a file -> parent not a dir\n\n# after\nsave_resource(url, 'out_dir', filename='img.png')  # destination is a directory","handlingStrategy":"validation","validationCode":"from pathlib import Path\ntarget = Path(destination) / (filename or 'x')\nassert not target.parent.exists() or target.parent.is_dir(), f'{target.parent} is a file'","typeGuard":null,"tryCatchPattern":"try:\n    save_resource(url, dest, filename=filename)\nexcept InvalidRequestError as e:\n    if 'must be a directory' in str(e):\n        raise ValueError(f'destination layout wrong: {e}') from e\n    raise","preventionTips":["Never pass a file path as destination when also supplying filename","Keep filenames flat (no subdirectory components) to avoid accidental nested targets"],"tags":["filesystem","path","validation"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}