{"record":{"id":"a348faece85f4b4e","repo":"PrefectHQ/fastmcp","slug":"cannot-load-spec-for-file-path","errorCode":null,"errorMessage":"Cannot load spec for {file_path}","messagePattern":"Cannot load spec for (.+?)","errorType":"exception","errorClass":"ImportError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/providers/filesystem_discovery.py","lineNumber":250,"sourceCode":"        existing = sys.modules.get(stem)\n        if existing is not None and getattr(existing, \"__file__\", None) != str(\n            file_path\n        ):\n            module_name = f\"_fastmcp_{stem}_{hashlib.sha1(str(file_path).encode()).hexdigest()[:12]}\"\n        else:\n            module_name = stem\n\n        # Temporarily add parent to sys.path so module-level sibling imports resolve.\n        # Safe to remove after exec_module: all top-level imports are resolved by then,\n        # and sibling files imported as side effects are already in sys.modules.\n        path_added = parent_dir not in sys.path\n        if path_added:\n            sys.path.insert(0, parent_dir)\n\n        try:\n            spec = importlib.util.spec_from_file_location(module_name, file_path)\n            if spec is None or spec.loader is None:\n                raise ImportError(f\"Cannot load spec for {file_path}\")\n\n            existing = sys.modules.get(module_name)\n            if existing is not None:\n                # Re-exec in place rather than importlib.reload: reload() re-finds\n                # the module by name via sys.path, which fails for private keys\n                # (the file is tool.py, not _fastmcp_tool_xxx.py).\n                existing.__spec__ = spec\n                existing.__loader__ = spec.loader\n                existing.__file__ = str(file_path)\n                try:\n                    spec.loader.exec_module(existing)\n                except Exception as e:\n                    raise ImportError(\n                        f\"Failed to reload module {file_path}: {e}\"\n                    ) from e\n                return existing\n\n            module = importlib.util.module_from_spec(spec)","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/providers/filesystem_discovery.py#L232-L268","documentation":"`importlib.util.spec_from_file_location` returned None or a spec without a loader, so Python could not determine how to load the file. FastMCP raises this immediately rather than failing later with a confusing AttributeError.","triggerScenarios":"Passing a path whose extension has no registered loader (e.g. `.txt`, extension-less file, `.pyc` without source context), a directory instead of a file, or a corrupted/unreadable file.","commonSituations":"Discovery glob picking up non-Python files; a file deleted or truncated between discovery and import; pointing the loader at a directory instead of a `.py` file.","solutions":["Confirm the path points to a real `.py` file (`path.is_file()` and suffix == '.py') before discovery/import","Exclude non-Python files from the discovery glob/pattern","Re-check the file exists and is readable — it may have been deleted or moved after discovery","If importing extensionless files, register a loader or rename with a `.py` extension"],"exampleFix":"// before\nimport_module_from_file(Path('plugins/tool'))  # no extension\n// after\nimport_module_from_file(Path('plugins/tool.py'))","handlingStrategy":"validation","validationCode":"from pathlib import Path\nassert (p := Path(fp)).is_file() and p.suffix == '.py', f'not a loadable .py file: {p}'","typeGuard":"def loadable_module_file(p) -> bool:\n    from pathlib import Path\n    p = Path(p)\n    return p.is_file() and p.suffix == '.py'","tryCatchPattern":"try:\n    mod = provider.import_module_from_file(path)\nexcept ImportError as e:\n    if 'Cannot load spec' in str(e): skip_file(path)","preventionTips":["Filter discovery globs to '*.py' files only","Confirm files exist before import (discovery and import can be separated in time)","Never point the loader at directories or bytecode without source"],"tags":["python","import","spec","file-loading"],"backgroundTag":"import-spec-load-failed","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}