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

  1. Decorate the raw callable with the @lifespan decorator before composing.
  2. Wrap a context manager with ContextManagerLifespan(cm) before using |.
  3. 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

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


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