PrefectHQ/fastmcp · error · RuntimeError
Extension {self.identifier!r} is not bound to a FastMCP serv
Error message
Extension {self.identifier!r} is not bound to a FastMCP server; register it with FastMCP.add_extension() before use. What it means
ServerExtension.server is a weakly-referenced property pointing at the FastMCP instance the extension was registered on. Accessing it before FastMCP.add_extension() has been called (or after the server was garbage-collected) finds no live server and raises RuntimeError.
Source
Thrown at fastmcp_slim/fastmcp/server/extensions.py:154
if identifier is not None:
validate_extension_identifier(identifier, owner=cls.__name__)
def _bind(self, server: FastMCP) -> None:
"""Bind this extension to its FastMCP instance (called by `add_extension`).
A weak reference avoids a reference cycle between the server and its
extensions. Per-instance identifiers are validated here.
"""
validate_extension_identifier(self.identifier, owner=type(self).__name__)
self._server_ref = weakref.ref(server)
@property
def server(self) -> FastMCP:
"""The FastMCP server this extension is registered on.
Handlers, interceptors, and lifespan code reach the component registry,
`Context`, and auth scope through here. Raises if the extension has not
been registered with `FastMCP.add_extension()`.
"""
ref = self._server_ref
server = ref() if ref is not None else None
if server is None:
raise RuntimeError(
f"Extension {self.identifier!r} is not bound to a FastMCP server; "
"register it with FastMCP.add_extension() before use."
)
return server
def settings(self) -> dict[str, Any]:
"""Per-extension settings advertised at `capabilities.extensions[identifier]`.
An empty dict (the default) advertises the extension with no settings.
"""
return {}
def methods(self) -> Sequence[MethodBinding]:View on GitHub (pinned to 1f02114297)
Solutions
- Register the extension first: server = FastMCP(...); server.add_extension(ext), then access ext.server.
- Only access ext.server from within handlers/interceptors/lifespan, which run after registration.
- Keep a direct reference to your FastMCP instance in application code instead of reaching through the extension.
- If the server may have been garbage collected, re-create/re-register the extension on the live server.
Example fix
// before
ext = MyExtension()
registry = ext.server._component_manager # RuntimeError: not bound
// after
server = FastMCP("demo")
server.add_extension(ext)
registry = ext.server._component_manager Defensive patterns
Strategy: try-catch
Validate before calling
def extension_bound(ext) -> bool:
ref = getattr(ext, "_server_ref", None)
return bool(ref and ref() is not None) Type guard
def is_bound(ext) -> bool:
return ext._server_ref is not None and ext._server_ref() is not None Try / catch
try:
server = ext.server
except RuntimeError:
server = None # defer until after add_extension() Prevention
- Always call FastMCP.add_extension(ext) before touching ext.server
- Access ext.server only from handlers/interceptors/lifespan callbacks
- Keep a strong reference to the FastMCP instance so the weakref stays alive
When it happens
Trigger: Calling extension.server inside handler/interceptor/lifespan code before registration; instantiating an extension and probing .server immediately; the server object being dropped so the weakref is dead.
Common situations: Unit tests constructing an extension in isolation; module-level extension objects used at import time before the server exists; holding extensions across server restarts.
Related errors
- JWT issuer not initialized. Ensure get_routes() is called be
- Client is not connected. Use the 'async with client:' contex
- ClientGroup is already connected
- FastMCP instance is no longer available
- No active context found.
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/bce14fd38b7a6fa2.
Report an issue: GitHub.