PrefectHQ/fastmcp · error · TypeError

Version must be a string, int, or float, got {type(v).__name

Error message

Version must be a string, int, or float, got {type(v).__name__}: {v!r}

What it means

Companion to the bool case: _coerce_version rejects any version value that is not str, int, or float (e.g. list, dict, None-adjacent objects) with a TypeError reporting the actual type. Versions are the component identity suffix (part of .key) so they must be scalar.

Source

Thrown at fastmcp_slim/fastmcp/utilities/components.py:59

    if maybe_set is None:
        return set()
    if isinstance(maybe_set, set):
        return maybe_set
    return set(maybe_set)


def _coerce_version(v: str | int | float | None) -> str | None:
    """Coerce version to string, accepting int, float, or str.

    Raises TypeError for non-scalar types (list, dict, set, etc.).
    Raises ValueError if version contains '@' (used as key delimiter).
    """
    if v is None:
        return None
    if isinstance(v, bool):
        raise TypeError(f"Version must be a string, int, or float, got bool: {v!r}")
    if not isinstance(v, (str, int, float)):
        raise TypeError(
            f"Version must be a string, int, or float, got {type(v).__name__}: {v!r}"
        )
    version = str(v)
    if "@" in version:
        raise ValueError(
            f"Version string cannot contain '@' (used as key delimiter): {version!r}"
        )
    return version


class FastMCPComponent(FastMCPBaseModel):
    """Base class for FastMCP tools, prompts, resources, and resource templates."""

    KEY_PREFIX: ClassVar[str] = ""

    def __init_subclass__(cls, **kwargs: Any) -> None:
        super().__init_subclass__(**kwargs)
        # Warn if a subclass doesn't define KEY_PREFIX (inherited or its own)

View on GitHub (pinned to 1f02114297)

Solutions

  1. Convert the version to a string/int/float before passing it, e.g. version=".".join(map(str, semver_list))
  2. Fix the config source so version is a scalar
  3. Use a simple string version like "1.2.0"

Example fix

// before
version=[1, 2, 0]
Tool(fn, version=version)

// after
version="1.2.0"
Tool(fn, version=version)
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(v, (str, int, float)) or isinstance(v, bool):
    v = str(v)  # or normalize before passing

Type guard

def is_scalar_version(v: object) -> bool:
    return isinstance(v, (str, int, float)) and not isinstance(v, bool)

Try / catch

try:
    tool = Tool(fn, version=v)
except TypeError as e:
    if "Version must be" in str(e):
        tool = Tool(fn, version=str(v))
    else:
        raise

Prevention

When it happens

Trigger: Passing a non-scalar like a list, dict, tuple, or datetime as the version argument to a FastMCP component or transform constructor.

Common situations: Passing a parsed semver list like [1, 2, 0] from config; passing a settings object attribute that is a dict; forgetting to stringify a custom version type.

Related errors


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