PrefectHQ/fastmcp · error · ValueError
cache_scope requires cache_ttl; a scope without a TTL does n
Error message
cache_scope requires cache_ttl; a scope without a TTL does not enable caching
What it means
build_cache_hints raises ValueError when cache_scope is provided to FastMCP(...) but cache_ttl is None. The client gates caching on the presence of a TTL, so a scope alone would be silently meaningless — the library rejects it instead of ignoring it.
Source
Thrown at fastmcp_slim/fastmcp/server/caching.py:50
) -> dict[CacheableMethod, CacheHint] | None:
"""Build the per-method `CacheHint` map for the SDK low-level server.
`cache_ttl` is in seconds and is converted to the wire's milliseconds. When
`cache_ttl` is `None` the server emits no hint, so its wire output is
identical to a server that never set one; a `cache_scope` given without a
`cache_ttl` is meaningless (the client gates caching on the presence of a
TTL) and is rejected rather than silently ignored.
Returns `None` when no hint is set, or a map applying the same hint to every
SDK-cacheable method otherwise.
Raises:
ValueError: If `cache_ttl` is not positive, or if `cache_scope` is set
without `cache_ttl`.
"""
if cache_ttl is None:
if cache_scope is not None:
raise ValueError(
"cache_scope requires cache_ttl; a scope without a TTL does not "
"enable caching"
)
return None
if cache_ttl <= 0:
raise ValueError(f"cache_ttl must be a positive integer, got {cache_ttl}")
hint = CacheHint(ttl_ms=cache_ttl * 1000, scope=cache_scope or "private")
return dict.fromkeys(get_args(CacheableMethod), hint)
View on GitHub (pinned to 1f02114297)
Solutions
- Provide a positive integer cache_ttl in seconds alongside cache_scope, e.g. FastMCP(cache_ttl=300, cache_scope="public").
- If caching is not wanted, remove cache_scope as well — pass neither.
- Validate your config loading so cache_ttl defaults to an integer, not None, when scope is configured.
- Catch ValueError at server-construction time and fail fast with a clear config error.
Example fix
// before
mcp = FastMCP("server", cache_scope="public") # ValueError
// after
mcp = FastMCP("server", cache_ttl=300, cache_scope="public") Defensive patterns
Strategy: validation
Validate before calling
def check_cache_config(ttl: int | None, scope: str | None) -> None:
if scope is not None and ttl is None:
raise ValueError("cache_scope requires cache_ttl")
if ttl is not None and ttl <= 0:
raise ValueError("cache_ttl must be positive") Type guard
def has_valid_cache_hint(ttl: int | None, scope: str | None) -> bool:
return scope is None or (isinstance(ttl, int) and ttl > 0) Try / catch
try:
mcp = FastMCP("server", cache_ttl=cfg.ttl, cache_scope=cfg.scope)
except ValueError as e:
raise ConfigError(f"invalid cache settings: {e}") from e Prevention
- Always pair cache_scope with a positive integer cache_ttl
- Validate config parsing so a missing TTL env var doesn't become None while scope survives
- Fail fast at startup — this error fires at server construction, before any request
When it happens
Trigger: FastMCP(cache_scope="public") without cache_ttl; programmatically calling build_cache_hints(None, "private"); config where cache_ttl is parsed out (e.g. env var missing yields None) but cache_scope is set.
Common situations: Setting cache_scope from a config file while forgetting cache_ttl; typo like cache_ttl="0" string coercion failing upstream and arriving as None; migrating from an older FastMCP version where scope behaved differently.
Related errors
- Invalid URL: {url}
- At least one of 'tools', 'include_tags', or 'exclude_tags' i
- No MCP servers defined in the config: {file_path}
- identity_assertion.trusted_issuers must not be empty
- trusted_issuers entries must be non-empty strings
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/6ef934afa29d77c2.
Report an issue: GitHub.