PrefectHQ/fastmcp · error · ImportError

Failed to reload module {file_path}: {e}

Error message

Failed to reload module {file_path}: {e}

What it means

When the module was already imported (hot-reload path), FastMCP re-execs it in place by resetting `__spec__`/`__loader__`/`__file__` and calling `spec.loader.exec_module`. Any exception during that re-execution — not just ImportError — is wrapped in this ImportError with the file path.

Source

Thrown at fastmcp_slim/fastmcp/server/providers/filesystem_discovery.py:263

            sys.path.insert(0, parent_dir)

        try:
            spec = importlib.util.spec_from_file_location(module_name, file_path)
            if spec is None or spec.loader is None:
                raise ImportError(f"Cannot load spec for {file_path}")

            existing = sys.modules.get(module_name)
            if existing is not None:
                # Re-exec in place rather than importlib.reload: reload() re-finds
                # the module by name via sys.path, which fails for private keys
                # (the file is tool.py, not _fastmcp_tool_xxx.py).
                existing.__spec__ = spec
                existing.__loader__ = spec.loader
                existing.__file__ = str(file_path)
                try:
                    spec.loader.exec_module(existing)
                except Exception as e:
                    raise ImportError(
                        f"Failed to reload module {file_path}: {e}"
                    ) from e
                return existing

            module = importlib.util.module_from_spec(spec)
            sys.modules[module_name] = module

            try:
                spec.loader.exec_module(module)
            except Exception as e:
                # Clean up sys.modules on failure
                sys.modules.pop(module_name, None)
                raise ImportError(f"Failed to execute module {file_path}: {e}") from e

            return module
        finally:
            if path_added:
                with contextlib.suppress(ValueError):

View on GitHub (pinned to 1f02114297)

Solutions

  1. Fix the error in the source file — the chained cause shows the original exception and line
  2. Validate the file compiles before saving: `python -m py_compile plugin.py`
  3. Avoid module-level side effects; move connection/startup logic into functions
  4. Re-trigger discovery once the file is fixed so the module re-executes cleanly

Example fix

// before
# plugin.py (edited)
def tool(): ...
run_setup()  # NameError: run_setup was deleted
// after
# plugin.py
def tool(): ...  # remove the call or re-import run_setup
Defensive patterns

Strategy: try-catch

Validate before calling

import py_compile
py_compile.compile('plugin.py', doraise=True)  # raises on syntax errors before reload

Try / catch

try:
    mod = provider.import_module_from_file(path)  # reload path
except ImportError as e:
    log.error('reload of %s failed: %s', path, e.__cause__)
    # keep serving the previous module version; fix source before retrying

Prevention

When it happens

Trigger: Reload of an existing module whose new source raises at import time: NameError, SyntaxError, missing dependency, or module-level code that fails (e.g. connecting to a service).

Common situations: Editing a tool file and introducing a typo or undefined name; reloading after renaming an imported helper; module-level side effects (DB connections) failing on reload.

Related errors


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