PrefectHQ/fastmcp · error · NotADirectoryError
Not a directory: {self.path}
Error message
Not a directory: {self.path} What it means
DirectoryResource.list_files() raises NotADirectoryError when the configured `path` exists but is a file (or other non-directory), so a directory listing cannot be produced. This separates 'path missing' (210) from 'path exists but wrong kind of node'.
Source
Thrown at fastmcp_slim/fastmcp/resources/types.py:165
@property
def _async_path(self) -> AsyncPath:
return AsyncPath(self.path)
@pydantic.field_validator("path")
@classmethod
def validate_absolute_path(cls, path: Path) -> Path:
"""Ensure path is absolute."""
if not path.is_absolute():
raise ValueError("Path must be absolute")
return path
async def list_files(self) -> list[Path]:
"""List files in the directory."""
if not await self._async_path.exists():
raise FileNotFoundError(f"Directory not found: {self.path}")
if not await self._async_path.is_dir():
raise NotADirectoryError(f"Not a directory: {self.path}")
pattern = self.pattern or "*"
glob_fn = self._async_path.rglob if self.recursive else self._async_path.glob
try:
return [Path(p) async for p in glob_fn(pattern) if await p.is_file()]
except Exception as e:
raise ResourceError(f"Error listing directory {self.path}") from e
@override
async def read(self) -> ResourceResult:
"""Read the directory listing."""
try:
files: list[Path] = await self.list_files()
file_list = [str(f.relative_to(self.path)) for f in files]
content = json.dumps({"files": file_list}, indent=2)View on GitHub (pinned to 1f02114297)
Solutions
- Point DirectoryResource.path at a directory instead of a file.
- If you meant to expose a single file, use FileResource (or the appropriate resource type) rather than DirectoryResource.
- Add a startup assertion: path.is_dir() before registering the resource.
- If a symlink is involved, confirm it resolves to a directory (Path.resolve().is_dir()).
Example fix
// before
DirectoryResource(path=Path('/data/config.json'), name='config') # it's a file
// after
from fastmcp.resources import FileResource
FileResource(path=Path('/data/config.json'), name='config') Defensive patterns
Strategy: validation
Validate before calling
from pathlib import Path
path = Path('/data/config.json')
if path.exists() and not path.is_dir():
raise NotADirectoryError(f'Expected a directory: {path}') Try / catch
try:
result = await resource.read()
except NotADirectoryError as e:
logger.error('path is not a directory: %s', e) Prevention
- Check path.is_dir() before constructing DirectoryResource.
- Use FileResource for single files.
- Resolve symlinks and verify the target type.
When it happens
Trigger: Construct DirectoryResource with `path` set to an existing regular file (or symlink to a file, socket, etc.); the is_dir() check at fastmcp_slim/fastmcp/resources/types.py:165 fails when read()/list_files() is called.
Common situations: Config points at a single file (e.g. /etc/passwd or a config.json) instead of its parent directory; path is a symlink whose target changed to a file; copy/paste error swapping a file path for the directory that contains it.
Related errors
- Error reading file {self.path}
- Directory not found: {self.path}
- Error listing directory {self.path}
- Error reading directory {self.path}
- File not found: {self.file_path}
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/970ec08ec4c99929.
Report an issue: GitHub.