PrefectHQ/fastmcp · error · ValueError

Invalid root: {r}

Error message

Invalid root: {r}

What it means

ValueError from `convert_roots_list` when an item in a roots list is not one of the accepted root representations (mcp_types.Root, pydantic.FileUrl, or str). The library cannot coerce the value into an MCP Root.

Source

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

RootsList: TypeAlias = list[str] | list[mcp_types.Root] | list[str | mcp_types.Root]

RootsHandler: TypeAlias = (
    Callable[[RequestContext[ClientSession, LifespanContextT]], RootsList]
    | Callable[[RequestContext[ClientSession, LifespanContextT]], Awaitable[RootsList]]
)


def convert_roots_list(roots: RootsList) -> list[mcp_types.Root]:
    roots_list = []
    for r in roots:
        if isinstance(r, mcp_types.Root):
            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)

View on GitHub (pinned to 1f02114297)

Solutions

  1. Convert values to `str` before passing (str(Path(...))) — strings are coerced via pydantic.FileUrl
  2. Ensure strings are valid file:// URLs, e.g. `Path(p).absolute().as_uri()`
  3. Wrap non-file URLs appropriately or drop them — roots must be file:// URIs
  4. Use `mcp_types.Root(uri=pydantic.FileUrl(...))` explicitly for full control

Example fix

// before
client.set_roots([Path("/data")])  # ValueError

// after
client.set_roots([Path("/data").absolute().as_uri()])
Defensive patterns

Strategy: validation

Validate before calling

import pydantic
from mcp import types as mcp_types

def valid_root(r) -> bool:
    return (isinstance(r, mcp_types.Root)
            or isinstance(r, pydantic.FileUrl)
            or (isinstance(r, str) and _is_file_url(r)))

def _is_file_url(s: str) -> bool:
    try:
        pydantic.FileUrl(s); return True
    except Exception:
        return False

Type guard

def is_root(r) -> bool:
    import pydantic
    from mcp import types as mcp_types
    if isinstance(r, mcp_types.Root) or isinstance(r, pydantic.FileUrl):
        return True
    if isinstance(r, str):
        try:
            pydantic.FileUrl(r); return True
        except Exception:
            return False
    return False

Prevention

When it happens

Trigger: Passing a roots list to `client.set_roots(...)` (through `create_roots_callback` → `_create_roots_callback_from_roots`) containing e.g. a pathlib.Path, an `AnyUrl` that is not a FileUrl (http://...), an int, or None.

Common situations: Passing `pathlib.Path` objects from filesystem walking code; passing http(s) URLs instead of file:// URLs; passing directories from config that were never converted to strings.

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/53cd6bc58f9311c7. Report an issue: GitHub.