PrefectHQ/fastmcp · error · FileNotFoundError
File not found: {self.file_path}
Error message
File not found: {self.file_path} What it means
SkillFileResource.read() raises FileNotFoundError when the resolved file inside a skill folder does not exist on disk. The path is first validated by safe_join (rejecting traversal/absolute injection), then checked with full_path.exists() before reading. This means the URI resolved to a real resource entry but the underlying file was deleted, renamed, or never created.
Source
Thrown at fastmcp_slim/fastmcp/server/providers/skills/skill_provider.py:163
def get_meta(self) -> dict[str, Any]:
meta = super().get_meta()
fastmcp = cast(dict[str, Any], meta["fastmcp"])
fastmcp["skill"] = {
"name": self.skill_info.name,
}
return meta
async def read(self) -> str | bytes | ResourceResult:
"""Read the file content."""
# Security: reject traversal, absolute-path injection, null bytes, and
# symlink escapes before touching the filesystem.
try:
full_path = safe_join(self.skill_info.path, self.file_path)
except PathEscapeError as e:
raise ValueError(f"Invalid path: {e}") from e
if not full_path.exists():
raise FileNotFoundError(f"File not found: {self.file_path}")
mime_type, _ = mimetypes.guess_type(str(full_path))
if mime_type and mime_type.startswith("text/"):
return full_path.read_text(encoding="utf-8")
else:
return full_path.read_bytes()
# -----------------------------------------------------------------------------
# SkillProvider - handles a SINGLE skill folder
# -----------------------------------------------------------------------------
class SkillProvider(Provider):
"""Provider that exposes a single skill folder as MCP resources.
Each skill folder must contain a main file (default: SKILL.md) and may
contain additional supporting files.View on GitHub (pinned to 1f02114297)
Solutions
- List the current files in the skill folder (skill://{name}/_manifest resource) and use an existing file_path in the URI
- Re-add or restore the missing file in the skill directory on disk
- Refresh the server's resource listing (restart or re-enumerate) if the cache is stale after files changed
- Verify the skill_path configured on SkillProvider points at the directory you think it does
Example fix
// before: guessing a supporting file name
await client.read_resource('skill://my-skill/examples/demo.py')
// after: discover real file names from the manifest first
manifest = await client.read_resource('skill://my-skill/_manifest')
# pick a file_path actually present in the manifest, then read it
await client.read_resource('skill://my-skill/SKILL.md') Defensive patterns
Strategy: try-catch
Validate before calling
import os
p = os.path.join(skill_dir, 'examples/demo.py')
assert not os.path.isabs('examples/demo.py') and os.path.exists(p), 'file missing from skill folder' Type guard
from pathlib import Path
def file_exists_in_skill(skill_dir: Path, rel: str) -> bool:
candidate = (skill_dir / rel).resolve()
return candidate.is_file() and candidate.is_relative_to(skill_dir.resolve()) Try / catch
try:
content = await client.read_resource('skill://my-skill/examples/demo.py')
except FileNotFoundError:
manifest = await client.read_resource('skill://my-skill/_manifest')
content = await pick_existing_file(manifest) Prevention
- Read the _manifest resource to discover valid file names instead of guessing
- Keep skill folders immutable during a server session; redeploy atomically
- Check os.path.exists on the skill folder after deploys before serving
- Pin/sync skill directories from a single source of truth
When it happens
Trigger: Reading a skill:// resource whose file_path points to a file that no longer exists — e.g. the skill folder was modified after the provider enumerated it, the client requests a supporting file that isn't in the skill directory, or a stale ResourceTemplate match references a removed file.
Common situations: Skill directories regenerated or cleaned by a build/deploy step while a server holds cached resource listings; typo'd file names in client URIs; skills synced from git where files were moved; container images that stripped non-main files from the skill folder.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- Error reading file {self.path}
- Directory not found: {self.path}
- Not a directory: {self.path}
- Error listing directory {self.path}
- Error reading directory {self.path}
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/86b3e7cc48fb2583.
Report an issue: GitHub.