PrefectHQ/fastmcp · error · ResourceError

Error listing directory {self.path}

Error message

Error listing directory {self.path}

What it means

DirectoryResource.list_files() wraps any exception from the glob iteration (PermissionError, OSError during traversal, bad pattern, etc.) into a ResourceError with the message 'Error listing directory {path}' and chains the original exception. It is the catch-all for failures during the recursive/non-recursive glob and per-file is_file() checks.

Source

Thrown at fastmcp_slim/fastmcp/resources/types.py:173

        """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)
            return ResourceResult(
                contents=[ResourceContent(content=content, mime_type=self.mime_type)]
            )
        except Exception as e:
            raise ResourceError(f"Error reading directory {self.path}") from e

View on GitHub (pinned to 1f02114297)

Solutions

  1. Inspect the chained cause (`raise ... from e`) — log or catch ResourceError and read `e.__cause__` to find the real error (usually PermissionError).
  2. Fix filesystem permissions (chmod/chown) so the server process can read the directory.
  3. Validate/simplify the `pattern` configured on the resource.
  4. Reduce scope (disable recursive, narrower pattern) if traversal is hitting limits.

Example fix

// before
files = await resource.read()  # opaque ResourceError

// after
try:
    files = await resource.read()
except ResourceError as e:
    logging.error("listing failed: %r", e.__cause__)
Defensive patterns

Strategy: try-catch

Validate before calling

import os
path = '/data/reports'
if not os.access(path, os.R_OK | os.X_OK):
    raise PermissionError(f'Cannot read directory: {path}')

Try / catch

try:
    files = await resource.list_files()
except ResourceError as e:
    cause = e.__cause__
    logger.error('listing %s failed: %r', resource.path, cause)

Prevention

When it happens

Trigger: Call read()/list_files() on a DirectoryResource (types.py:173) when glob raises — e.g. the directory is unreadable due to permissions, a glob pattern is malformed, or an entry disappears mid-iteration (race with deletion).

Common situations: Running as a user without read/execute permission on the directory or subdirectories; running recursive listings over huge trees that hit OS limits; another process deleting files while the listing runs; invalid pattern strings for the glob backend.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/1cb06affdad32063. Report an issue: GitHub.