crewAIInc/crewAI · error · ValueError

Path is not a directory: {source_ref}

Error message

Path is not a directory: {source_ref}

What it means

Raised by DirectoryLoader.load() when the source_ref exists on disk but is not a directory (os.path.isdir fails). This catches the case where a regular file, symlink to a file, or special file is passed where a directory tree is expected. The message distinguishes it from the not-found case so the caller knows the path resolves but to the wrong kind of object.

Source

Thrown at lib/crewai-tools/src/crewai_tools/rag/loaders/directory_loader.py:32

            source_content: Directory path or URL to a directory listing
            **kwargs: Additional options:
                - recursive: bool (default True) - Whether to search recursively
                - include_extensions: list - Only include files with these extensions
                - exclude_extensions: list - Exclude files with these extensions
                - max_files: int - Maximum number of files to process
        """
        source_ref = source_content.source_ref

        if source_content.is_url():
            raise ValueError(
                "URL directory loading is not supported. Please provide a local directory path."
            )

        if not os.path.exists(source_ref):
            raise FileNotFoundError(f"Directory does not exist: {source_ref}")

        if not os.path.isdir(source_ref):
            raise ValueError(f"Path is not a directory: {source_ref}")

        return self._process_directory(source_ref, kwargs)

    def _process_directory(self, dir_path: str, kwargs: dict[str, Any]) -> LoaderResult:
        recursive: bool = kwargs.get("recursive", True)
        include_extensions: list[str] | None = kwargs.get("include_extensions", None)
        exclude_extensions: list[str] | None = kwargs.get("exclude_extensions", None)
        max_files: int | None = kwargs.get("max_files", None)

        files = self._find_files(
            dir_path, recursive, include_extensions, exclude_extensions
        )

        if max_files is not None and len(files) > max_files:
            files = files[:max_files]

        all_contents = []
        processed_files = []

View on GitHub (pinned to 754d7323be)

Solutions

  1. If you meant to load one file, use the matching file loader (TextLoader, PDFLoader, DOCXLoader) or point DirectoryLoader at the containing directory.
  2. If you meant a directory, correct the path so it names the folder, not a file inside it.
  3. Add a pre-check with os.path.isdir(source) and fail with your own clearer message.

Example fix

# before
result = DirectoryLoader().load(SourceContent('reports/q3.pdf'))

# after
result = PDFLoader().load(SourceContent('reports/q3.pdf'))
# or, to ingest the whole folder:
result = DirectoryLoader().load(SourceContent('reports'))
Defensive patterns

Strategy: type-guard

Validate before calling

import os\n\ndef ensure_directory(path: str) -> bool:\n    return os.path.isdir(path) and not os.path.isfile(path)

Type guard

def is_directory_source(s: str) -> bool:\n    return os.path.isdir(s)

Try / catch

try:\n    result = loader.load(source)\nexcept ValueError as e:\n    if 'not a directory' in str(e):\n        # route single files to a file loader\n        result = pick_file_loader(s).load(source)\n    else:\n        raise

Prevention

When it happens

Trigger: Passing a path to a single file (e.g. notes.txt) to DirectoryLoader instead of a file loader like TextLoader or PDFLoader; passing a symlink that points to a file; passing a device/socket path.

Common situations: Copy-paste from a file-loader example into DirectoryLoader; user-supplied source strings where a filename is given where a folder is expected; broken symlinks that resolve to files; picking DirectoryLoader from the loader registry because it accepts any 'source'.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/7080497b8e8ff2fe. Report an issue: GitHub.