PrefectHQ/fastmcp · error · TypeError
Cannot compose Lifespan with {type(other).__name__}. Use @li
Error message
Cannot compose Lifespan with {type(other).__name__}. Use @lifespan decorator or wrap with ContextManagerLifespan(). What it means
Lifespan.__or__ composes two Lifespan instances into a ComposedLifespan. Composing with anything that is not a Lifespan instance (e.g. a raw async context manager, an async generator function, or None) raises TypeError, telling you to decorate the callable with @lifespan or wrap it in ContextManagerLifespan().
Source
Thrown at fastmcp_slim/fastmcp/server/lifespan.py:103
The lifespan context dict.
"""
async with asynccontextmanager(self._fn)(server) as result:
yield result if result is not None else {}
def __or__(self, other: Lifespan) -> ComposedLifespan:
"""Compose with another lifespan using the | operator.
Args:
other: Another Lifespan instance.
Returns:
A ComposedLifespan that runs both lifespans.
Raises:
TypeError: If other is not a Lifespan instance.
"""
if not isinstance(other, Lifespan):
raise TypeError(
f"Cannot compose Lifespan with {type(other).__name__}. "
f"Use @lifespan decorator or wrap with ContextManagerLifespan()."
)
return ComposedLifespan(self, other)
class ContextManagerLifespan(Lifespan):
"""Lifespan wrapper for already-wrapped context manager functions.
Use this for functions already decorated with @asynccontextmanager.
"""
_fn: LifespanContextManagerFn # Override type for this subclass
def __init__(self, fn: LifespanContextManagerFn) -> None:
"""Initialize with a context manager factory function."""
self._fn = fn
View on GitHub (pinned to 1f02114297)
Solutions
- Decorate the raw callable with the @lifespan decorator before composing.
- Wrap a context manager with ContextManagerLifespan(cm) before using |.
- Compose using only Lifespan instances on both sides of the | operator.
Example fix
// before combined = server_lifespan | my_cm # TypeError // after from fastmcp.server.lifespan import ContextManagerLifespan combined = server_lifespan | ContextManagerLifespan(my_cm)
Defensive patterns
Strategy: type-guard
Validate before calling
def is_composable(other) -> bool:
from fastmcp.server.lifespan import Lifespan
return isinstance(other, Lifespan) Type guard
def as_lifespan(other):
from fastmcp.server.lifespan import Lifespan, ContextManagerLifespan
if isinstance(other, Lifespan):
return other
return ContextManagerLifespan(other) Try / catch
try:
combined = a | b
except TypeError as e:
combined = a | ContextManagerLifespan(b) Prevention
- Decorate lifespan callables with @lifespan, not @asynccontextmanager, when composing
- Wrap raw context managers with ContextManagerLifespan before using |
- assert isinstance(x, Lifespan) at composition sites
When it happens
Trigger: Writing fastmcp_lifespan | my_asynccontextmanager, or lifespan_a | lifespan_b where lifespan_b is a plain function/context manager rather than a Lifespan object.
Common situations: Mixing the @lifespan decorator style with @asynccontextmanager-produced context managers; passing the undecorated function itself instead of its result; chaining with | in a pipeline where one element lost its wrapper.
Related errors
- Protocol mode for server {name!r} must be a string
- Unsupported content type: {type(content)}
- mcp_tool() got unexpected keyword argument(s): {sorted(unkno
- mcp_resource() got unexpected keyword argument(s): {sorted(u
- mcp_prompt() got unexpected keyword argument(s): {sorted(unk
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/eb06e2cd366de97f.
Report an issue: GitHub.