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
- Convert values to `str` before passing (str(Path(...))) — strings are coerced via pydantic.FileUrl
- Ensure strings are valid file:// URLs, e.g. `Path(p).absolute().as_uri()`
- Wrap non-file URLs appropriately or drop them — roots must be file:// URIs
- 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
- Always convert paths with Path(...).absolute().as_uri() before set_roots
- Never pass pathlib.Path or http:// URLs as roots
- Validate the whole list with is_root before calling set_roots
- Only pass file:// URIs — roots are filesystem-scoped by spec
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
- Invalid roots handler: {handler}
- INVALID_PARAMS
- mode must be 'legacy', 'auto', or one of {list(MODERN_PROTOC
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
- [{self.name}] Reached auto-pagination limit ({max_pages} pag
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/53cd6bc58f9311c7.
Report an issue: GitHub.