PrefectHQ/fastmcp · error · ResourceError

Error reading file {self.path}

Error message

Error reading file {self.path}

What it means

When FileResource.read() fails for any reason (file missing, permission denied, decode error, etc.), the original exception is wrapped in a ResourceError whose message names the path. The underlying cause is chained (raise ... from e), so inspect __cause__ to see the real OS-level error.

Source

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

        """Set is_binary based on mime_type if not explicitly set."""
        if is_binary:
            return True
        mime_type = info.data.get("mime_type", "text/plain")
        return not mime_type.startswith("text/")

    @override
    async def read(self) -> ResourceResult:
        """Read the file content."""
        try:
            if self.is_binary:
                content: str | bytes = await self._async_path.read_bytes()
            else:
                content = await self._async_path.read_text(encoding=self.encoding)
            return ResourceResult(
                contents=[ResourceContent(content=content, mime_type=self.mime_type)]
            )
        except Exception as e:
            raise ResourceError(f"Error reading file {self.path}") from e


class HttpResource(Resource):
    """A resource that reads from an HTTP endpoint."""

    url: str = Field(description="URL to fetch content from")
    mime_type: str = Field(
        default="application/json", description="MIME type of the resource content"
    )

    @override
    async def read(self) -> ResourceResult:
        """Read the HTTP content."""
        async with httpx2.AsyncClient() as client:
            response = await client.get(self.url)
            _ = response.raise_for_status()
            return ResourceResult(
                contents=[

View on GitHub (pinned to 1f02114297)

Solutions

  1. Check the chained cause (e.g. try/except ResourceError as e: print(e.__cause__)) to see the OS error and fix it - create the file or fix permissions
  2. Set is_binary=True and an appropriate binary mime_type for non-text files
  3. Verify the path exists and is readable at startup: Path(path).exists() and os.access(path, os.R_OK)
  4. Wrap reads in try/except ResourceError and return a helpful message to the client

Example fix

// before
content = await file_resource.read()  # ResourceError: Error reading file /data/x.csv
// after
try:
    result = await file_resource.read()
except ResourceError as e:
    logger.error(f"resource read failed: {e} (cause: {e.__cause__})")
    raise
Defensive patterns

Strategy: try-catch

Validate before calling

import os
from pathlib import Path
def assert_readable(p):
    path = Path(p)
    if not path.exists():
        raise FileNotFoundError(path)
    if not os.access(path, os.R_OK):
        raise PermissionError(path)

Type guard

def file_readable(p) -> bool:
    import os
    path = Path(p)
    return path.is_file() and os.access(path, os.R_OK)

Try / catch

try:
    result = await file_resource.read()
except ResourceError as e:
    logger.error("read failed for %s: %s", file_resource.path, e.__cause__)
    raise

Prevention

When it happens

Trigger: Calling read() (directly or via MCP resource read) on a FileResource whose path doesn't exist, isn't readable, was deleted after registration, or whose bytes don't decode with the configured encoding.

Common situations: File removed/moved after server start; running server as a user lacking read permission; encoding mismatch (binary file read as UTF-8 text); wrong is_binary flag for the content type.

Related errors


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