PrefectHQ/fastmcp · error · ValueError

URI template must contain at least one parameter

Error message

URI template must contain at least one parameter

What it means

A resource template is defined by substituting URI template parameters into the template string. A URI with no {parameter} placeholders would always resolve to the same string, making it a static resource rather than a template, so from_function rejects it.

Source

Thrown at fastmcp_slim/fastmcp/resources/template.py:485

        # same Python identifier (e.g. {user-id} and {user_id}).
        all_raw = raw_path_params | raw_query_params
        seen: dict[str, str] = {}
        for raw_name in sorted(all_raw):
            normalized = raw_name.replace("-", "_")
            if normalized in seen:
                raise ValueError(
                    f"URI template parameters '{seen[normalized]}' and "
                    f"'{raw_name}' both normalize to '{normalized}'. "
                    f"Use one or the other, not both."
                )
            seen[normalized] = raw_name

        path_params = {p.replace("-", "_") for p in raw_path_params}
        query_params = {p.replace("-", "_") for p in raw_query_params}
        all_uri_params = path_params | query_params

        if not all_uri_params:
            raise ValueError("URI template must contain at least one parameter")

        # Use wrapper to get user-facing parameters (excludes injected params)
        from fastmcp.server.dependencies import (
            transform_context_annotations,
            without_injected_parameters,
        )

        wrapper_fn = without_injected_parameters(fn)
        user_sig = inspect.signature(wrapper_fn)
        func_params = set(user_sig.parameters.keys())

        # Get required and optional function parameters
        required_params = {
            p
            for p in func_params
            if user_sig.parameters[p].default is inspect.Parameter.empty
            and user_sig.parameters[p].kind != inspect.Parameter.VAR_KEYWORD
        }

View on GitHub (pinned to 1f02114297)

Solutions

  1. Add at least one {param} placeholder to the URI template
  2. If the URI truly has no variable parts, register it as a plain Resource (e.g. FastMCP.add_resource / Resource.from_function) instead of a template
  3. Fix the placeholder syntax (must be {name}) so it isn't dropped

Example fix

// before
ResourceTemplate.from_function(get_data, uri_template="data://fixed")
// after
ResourceTemplate.from_function(get_data, uri_template="data://items/{item_id}")
Defensive patterns

Strategy: validation

Validate before calling

import re
def ensure_template_has_params(uri_template):
    if not re.search(r"\{[^}]+\}", uri_template):
        raise ValueError(f"{uri_template!r} has no {{param}}; use a plain Resource")

Type guard

def is_template(uri_template) -> bool:
    import re
    return bool(re.search(r"\{[^}]+\}", uri_template))

Try / catch

try:
    tpl = FunctionResourceTemplate.from_function(fn, uri_template=uri)
except ValueError as e:
    if "at least one parameter" in str(e):
        resource = Resource.from_function(fn, uri=uri)  # static fallback
    else:
        raise

Prevention

When it happens

Trigger: Calling ResourceTemplate.from_function with a URI template containing no {param} placeholders anywhere (path or query), e.g. uri_template="data://fixed".

Common situations: Typo like data://{param] or data://[param] that silently removes the placeholder; switching from Resource.add to a template but reusing a plain URI; building the template string programmatically and interpolating away the braces.

Related errors


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