{"record":{"id":"56d7558a7d31c320","repo":"pola-rs/polars","slug":"expected-a-file-path-path-r-is-a-directory","errorCode":null,"errorMessage":"expected a file path; {path!r} is a directory","messagePattern":"expected a file path; (.+?) is a directory","errorType":"exception","errorClass":"IsADirectoryError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/_utils/various.py","lineNumber":264,"sourceCode":"def arrlen(obj: Any) -> int | None:\n    \"\"\"Return length of (non-string/dict) sequence; returns None for non-sequences.\"\"\"\n    try:\n        return None if isinstance(obj, (str, bytes, dict)) else len(obj)\n    except TypeError:\n        return None\n\n\ndef normalize_filepath(path: str | Path, *, check_not_directory: bool = True) -> str:\n    \"\"\"Create a string path, expanding the home directory if present.\"\"\"\n    # don't use pathlib here as it modifies slashes (s3:// -> s3:/)\n    path = os.path.expanduser(path)  # noqa: PTH111\n    if (\n        check_not_directory\n        and os.path.exists(path)  # noqa: PTH110\n        and os.path.isdir(path)  # noqa: PTH112\n    ):\n        msg = f\"expected a file path; {path!r} is a directory\"\n        raise IsADirectoryError(msg)\n    return path\n\n\ndef parse_version(version: Sequence[str | int]) -> tuple[int, ...]:\n    \"\"\"Simple version parser; split into a tuple of ints for comparison.\"\"\"\n    if isinstance(version, str):\n        version = version.split(\".\")\n    return tuple(int(re.sub(r\"\\D\", \"\", str(v))) for v in version)\n\n\ndef ordered_unique(values: Sequence[Any]) -> list[Any]:\n    \"\"\"Return unique list of sequence values, maintaining their order of appearance.\"\"\"\n    seen: set[Any] = set()\n    add_ = seen.add\n    return [v for v in values if not (v in seen or add_(v))]\n\n\ndef deduplicate_names(names: Iterable[str]) -> list[str]:","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/_utils/various.py#L246-L282","documentation":"IsADirectoryError from normalize_filepath (py-polars/src/polars/_utils/various.py:255-265). Most polars IO entry points (read_csv, scan_parquet, read_ipc, etc.) pass the user's path through normalize_filepath with check_not_directory=True; if the expanded path exists on disk and is a directory, polars refuses it up front instead of letting the OS produce a confusing read failure. Multi-file directory reads are intentionally not implicit: use an explicit glob.","triggerScenarios":"pl.scan_csv('data/') or pl.read_parquet('my_folder') where the argument is a directory; also passing a Path object that points at a directory. Expansion of ~ happens first, so '~/data' that is a directory also raises.","commonSituations":"Assuming read_csv on a directory reads all contained CSVs (pandas-like glob assumption); configurable path inputs that resolve to a folder in one environment and a file in another; download scripts writing to a directory then passing it back as a file path.","solutions":["Pass a file path or an explicit glob pattern, e.g. pl.scan_csv('data/*.csv')","If you want one file per call, iterate: [pl.read_csv(p) for p in sorted(Path('data').glob('*.csv'))]","Fix the upstream path variable that accidentally holds a directory","For single-file inputs, validate with Path(p).is_file() before calling the reader"],"exampleFix":"# before\nlf = pl.scan_parquet('partitioned_table/')  # IsADirectoryError\n\n# after\nlf = pl.scan_parquet('partitioned_table/**/*.parquet')","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\ndef as_readable_path(p: str | Path) -> str:\n    p = Path(p).expanduser()\n    if p.is_dir():\n        raise IsADirectoryError(f'{p} is a directory; pass a file or glob')\n    return str(p)","typeGuard":null,"tryCatchPattern":"try:\n    df = pl.read_parquet(path)\nexcept IsADirectoryError:\n    df = pl.read_parquet(str(Path(path) / '**' / '*.parquet'))","preventionTips":["Validate Path(p).is_file() (or a glob) before IO calls on user-supplied paths","Use explicit globs ('dir/*.csv') for multi-file reads","Centralize path normalization in one helper for configurable inputs"],"tags":["polars","io","filepath","isadirectoryerror"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}