PrefectHQ/fastmcp · error · ImportError
Failed to import {module_name} from {file_path}: {e}
Error message
Failed to import {module_name} from {file_path}: {e} What it means
`import_module_from_file` imports a file as a submodule under a private package anchor (e.g. `_fastmcp_tool_xxx`). When `importlib.import_module(private_name)` raises ImportError — typically because a module imported *by* the target file cannot be resolved, or a relative import fails outside a proper package — it is re-raised with this message wrapping the original cause.
Source
Thrown at fastmcp_slim/fastmcp/server/providers/filesystem_discovery.py:199
# normally beneath it.
top_name = module_name.split(".")[0]
existing_top = sys.modules.get(top_name)
if existing_top is not None and not _package_path_matches(
existing_top, package_root
):
anchor = _private_package_prefix(package_root.parent)
if anchor not in sys.modules:
spec = ModuleSpec(anchor, loader=None, is_package=True)
anchor_module = importlib.util.module_from_spec(spec)
anchor_module.__path__ = [str(package_root.parent)]
sys.modules[anchor] = anchor_module
private_name = f"{anchor}.{module_name}"
try:
if private_name in sys.modules:
return importlib.reload(sys.modules[private_name])
return importlib.import_module(private_name)
except ImportError as e:
raise ImportError(
f"Failed to import {module_name} from {file_path}: {e}"
) from e
# Temporarily add package root's parent to sys.path for the import
package_parent = str(package_root.parent)
path_added = package_parent not in sys.path
if path_added:
sys.path.insert(0, package_parent)
try:
# If already imported, reload to pick up changes (for reload mode)
if module_name in sys.modules:
return importlib.reload(sys.modules[module_name])
return importlib.import_module(module_name)
except ImportError as e:
raise ImportError(
f"Failed to import {module_name} from {file_path}: {e}"
) from eView on GitHub (pinned to 1f02114297)
Solutions
- Read the chained `__cause__` (`raise ... from e`) to see which inner import failed and install/fix that dependency
- Move relative imports (`from . import x`) to absolute imports or ensure the file lives in a real package with `__init__.py`
- Verify sys.path contains the directory the plugin file expects (the loader temporarily adds the package parent, but transitive deps may live elsewhere)
- Test the file standalone with `python -c "import your_module"` to reproduce the import error outside FastMCP
Example fix
// before # plugin.py from .helpers import build # fails: not in a real package // after # plugin.py from helpers import build # absolute import, helpers.py next to plugin.py
Defensive patterns
Strategy: try-catch
Validate before calling
import ast, pathlib
def check_imports(path):
tree = ast.parse(pathlib.Path(path).read_text())
return [n for n in ast.walk(tree) if isinstance(n, ast.ImportFrom) and n.level > 0] Type guard
def is_importable_py_file(p):
return p.is_file() and p.suffix == '.py' Try / catch
try:
mod = provider.import_module_from_file(path)
except ImportError as e:
log.error('import failed for %s: cause=%r', path, e.__cause__) Prevention
- Use absolute imports in plugin/discovered files
- Install all plugin dependencies in the runtime environment
- Run `python -m py_compile` or an import smoke test on plugin files before deploying
When it happens
Trigger: Calling `discover_and_import` (or the tests) on a file whose module body does `import some_missing_lib` or uses `from . import x` when the file is not importable as a package; the private anchor package exists but the nested import fails.
Common situations: Discovered tool files with undeclared dependencies; files relying on relative imports but loaded outside a real package layout; PYTHONPATH missing a dependency; typos in imports inside hot-reloaded plugin files.
Related errors
- Failed to execute module {file_path}: {e}
- module {__name__!r} has no attribute {name!r}
- Cannot load spec for {file_path}
- Failed to reload module {file_path}: {e}
- module {__name__!r} has no attribute {name!r}
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/77ddf29dac97680b.
Report an issue: GitHub.