PrefectHQ/fastmcp · error · ValueError

Invalid roots handler: {handler}

Error message

Invalid roots handler: {handler}

What it means

ValueError from `create_roots_callback` when the `handler` argument is neither a list of roots nor a callable. The roots API accepts either a static list or a handler function; anything else is rejected.

Source

Thrown at fastmcp_slim/fastmcp/client/roots.py:42

            roots_list.append(r)
        elif isinstance(r, pydantic.FileUrl):
            roots_list.append(mcp_types.Root(uri=r))
        elif isinstance(r, str):
            roots_list.append(mcp_types.Root(uri=pydantic.FileUrl(r)))
        else:
            raise ValueError(f"Invalid root: {r}")
    return roots_list


def create_roots_callback(
    handler: RootsList | RootsHandler,
) -> ListRootsFnT:
    if isinstance(handler, list):
        return _create_roots_callback_from_roots(handler)
    elif callable(handler):
        return _create_roots_callback_from_fn(handler)
    else:
        raise ValueError(f"Invalid roots handler: {handler}")


def _create_roots_callback_from_roots(
    roots: RootsList,
) -> ListRootsFnT:
    roots = convert_roots_list(roots)

    async def _roots_callback(
        context: ClientRequestContext,
    ) -> mcp_types.ListRootsResult:
        return mcp_types.ListRootsResult(roots=roots)

    return _roots_callback


def _create_roots_callback_from_fn(
    fn: Callable[[RequestContext[ClientSession, LifespanContextT]], RootsList]
    | Callable[[RequestContext[ClientSession, LifespanContextT]], Awaitable[RootsList]],

View on GitHub (pinned to 1f02114297)

Solutions

  1. Wrap a single root in a list: `client.set_roots(["file:///data"])`
  2. If passing a handler, ensure it is callable (e.g. an async function taking no args and returning roots)
  3. Check that the variable isn't None due to a failed lookup/factory
  4. Convert tuples/generators to `list(...)` before passing

Example fix

// before
client.set_roots("file:///data")  # ValueError

// after
client.set_roots(["file:///data"])
Defensive patterns

Strategy: validation

Validate before calling

if not (isinstance(handler, list) or callable(handler)):
    raise TypeError(f"handler must be a list or callable, got {type(handler).__name__}")

Type guard

def is_valid_roots_handler(h) -> bool:
    return isinstance(h, list) or callable(h)

Prevention

When it happens

Trigger: Calling `client.set_roots(handler)` (or `_bind_restoring_handlers`) with a non-list, non-callable value such as a string, a single Root object, a dict, or None.

Common situations: Passing a single root string instead of a one-element list (`set_roots("file:///x")` instead of `set_roots(["file:///x"])`); accidentally passing a tuple or generator; a variable that was expected to hold a function but is None.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


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