PrefectHQ/fastmcp · error · ValueError
Cannot resolve tool reference: {fn!r}
Error message
Cannot resolve tool reference: {fn!r} What it means
_resolve_tool_ref converts a tool reference (name string, callable, or Tool object) into a ResolvedTool. When the passed object is neither a string, a Tool, nor a callable carrying a __name__ attribute, resolution fails and this ValueError is raised. It protects the @app.tool registry from unresolvable references.
Source
Thrown at fastmcp_slim/fastmcp/apps/app.py:109
fmeta: Any = None
try:
from fastmcp.decorators import get_fastmcp_meta
fmeta = get_fastmcp_meta(fn)
except Exception:
pass
if fmeta is not None:
name: str | None = getattr(fmeta, "name", None)
if name is not None:
return ResolvedTool(name=_prefix(name))
fn_name = getattr(fn, "__name__", None)
if fn_name is not None:
return ResolvedTool(name=_prefix(fn_name))
raise ValueError(f"Cannot resolve tool reference: {fn!r}")
return _resolve_tool_ref
def _dispatch_decorator(
name_or_fn: str | AnyFunction | None,
name: str | None,
register: Callable[[Any, str | None], Any],
decorator_name: str,
) -> Any:
"""Shared dispatch logic for @app.tool() and @app.ui() calling patterns."""
if inspect.isroutine(name_or_fn):
return register(name_or_fn, name)
if isinstance(name_or_fn, str):
if name is not None:
raise TypeError(
"Cannot specify both a name as first argument and as keyword argument."View on GitHub (pinned to 1f02114297)
Solutions
- Pass the tool's name as a plain string instead of the object.
- Ensure the callable has a __name__ attribute (apply functools.wraps in your decorator, or use the raw function).
- Pass the actual Tool instance rather than a wrapper around it.
Example fix
// before ref = functools.partial(my_tool, flag=True) app.tool_ref(ref) // after ref = functools.partial(my_tool, flag=True) ref.__name__ = my_tool.__name__ # or pass "my_tool" as a string app.tool_ref(ref)
Defensive patterns
Strategy: type-guard
Validate before calling
def is_resolvable_tool_ref(fn) -> bool:
if isinstance(fn, str):
return True
return callable(fn) and getattr(fn, "__name__", None) is not None Type guard
def is_tool_ref(obj) -> bool:
return isinstance(obj, str) or (callable(obj) and hasattr(obj, "__name__")) Try / catch
try:
resolved = register_tool_ref(fn)
except ValueError as e:
if "Cannot resolve tool reference" in str(e):
resolved = register_tool_ref(getattr(fn, "__name__", str(fn))) Prevention
- Use functools.wraps in custom decorators so __name__ survives
- Pass tool names as strings when you have a wrapper object
- Avoid passing partials/proxies directly as tool references
- Check hasattr(fn, '__name__') before registering
When it happens
Trigger: Passing an arbitrary object (e.g. a dict, a class instance, a functools.partial-wrapped callable without __name__, or a lambda decorated to strip __name__) to a tool reference/lookup API that calls _resolve_tool_ref.
Common situations: Wrapping functions with decorators (e.g. functools.partial, custom decorators without functools.wraps) so __name__ disappears; passing a Mock or proxy object in tests; passing an already-serialized tool dict instead of its name.
Related errors
- Cannot determine tool name for {fn!r}
- File {name!r} not found. Available: {available}
- File {f.get('name', '?')!r} exceeds max size ({_format_size(
- cache_ttl must be a positive integer, got {cache_ttl}
- ToolTransform has duplicate target name {target!r}: both {se
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/08f85a6e47ad0b94.
Report an issue: GitHub.