cocoindex-io/cocoindex · error · ValueError

Path is not a directory

Error message

Path is not a directory: {root_resolved}

What it means

localfs directory walking resolves the root path and requires it to be an existing directory. If the resolved root is not a directory (missing path, a file, or a broken symlink), _walk_sync raises ValueError before yielding any File objects. The error surfaces when iterating the DirSource (via __aiter__).

Solutions

  1. Fix the root path to point to an existing directory
  2. Create the directory if it is expected to exist (mkdir -p) before running the pipeline
  3. If the path is supposed to be a file source, use the appropriate file-based API instead of a directory walk
  4. Make the path absolute (pathlib.Path(...).resolve()) and verify is_dir() before wiring it into the source

Example fix

// before
source = localfs.walk_dir("./documets")  # typo -> not a directory
// after
root = pathlib.Path("./documents").resolve()
assert root.is_dir(), root
source = localfs.walk_dir(root, recursive=True, path_matcher=PatternFilePathMatcher(["**/*.md"]))
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
root = Path(path).resolve()
if not root.is_dir():
    raise NotADirectoryError(f"source root is not a directory: {root}")

Type guard

def is_existing_dir(p) -> bool:
    from pathlib import Path
    try:
        return Path(p).resolve().is_dir()
    except OSError:
        return False

Try / catch

try:
    async for f in source:
        ...
except ValueError as e:
    if str(e).startswith("Path is not a directory"):
        logger.error("fix --sourcedir: %s", e)
        sys.exit(2)
    raise

Prevention

When it happens

Trigger: Creating localfs.dir(...) / DirSource with a root path that does not exist, points to a regular file, or is a dangling symlink, then iterating it (async for ... in source, or walk_dir items consumed by mount_each).

Common situations: Typo in the source directory path; pointing the pipeline at a file instead of a directory; the directory was deleted/moved before the run; relative path resolved from an unexpected working directory (e.g. different cwd in CI).


AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08). Data as JSON: /api/errors/2271236453c949df. Report an issue: GitHub.

Appendix: source

Thrown at python/cocoindex/connectors/localfs/_source.py:110

        path: FilePath | Path | ContextKey[Path],
        *,
        live: bool = False,
        recursive: bool = False,
        path_matcher: FilePathMatcher | None = None,
        rescan_interval: datetime.timedelta | None = _DEFAULT_RESCAN_INTERVAL,
    ) -> None:
        self._root_path = to_file_path(path)
        self._live = live
        self._recursive = recursive
        self._path_matcher = path_matcher or MatchAllFilePathMatcher()
        self._rescan_interval = rescan_interval

    def _walk_sync(self) -> Iterator[File]:
        """Synchronously walk the directory and yield File objects (internal helper)."""
        root_resolved = self._root_path.resolve()

        if not root_resolved.is_dir():
            raise ValueError(f"Path is not a directory: {root_resolved}")

        dirs_to_process: list[Path] = [root_resolved]

        while dirs_to_process:
            current_dir = dirs_to_process.pop()

            try:
                entries = list(current_dir.iterdir())
            except PermissionError:
                continue

            subdirs: list[Path] = []

            for entry in entries:
                try:
                    relative_path = entry.relative_to(root_resolved)
                except ValueError:
                    # Should not happen, but skip if it does

View on GitHub (pinned to e84aa99b32)