PrefectHQ/fastmcp · error · ValueError

cache_ttl_seconds must be non-negative, got {ttl_seconds}

Error message

cache_ttl_seconds must be non-negative, got {ttl_seconds}

What it means

The TokenCache constructor validates its cache_ttl_seconds parameter at init time and raises ValueError when a negative number is passed. A negative TTL is meaningless (entries would be instantly expired), so the library rejects it eagerly rather than silently misbehaving. Pass None or 0 to disable caching instead.

Source

Thrown at fastmcp_slim/fastmcp/utilities/token_cache.py:78

    """

    def __init__(
        self,
        *,
        ttl_seconds: int | None = None,
        max_size: int | None = None,
    ) -> None:
        """Initialise the cache.

        Args:
            ttl_seconds: How long cached entries remain valid, in seconds.
                ``None`` or ``0`` disables caching entirely.
            max_size: Upper bound on the number of entries.  When the limit is
                reached, expired entries are swept first; if still full the
                oldest entry is evicted.  Defaults to 10 000.
        """
        if ttl_seconds is not None and ttl_seconds < 0:
            raise ValueError(
                f"cache_ttl_seconds must be non-negative, got {ttl_seconds}"
            )
        if max_size is not None and max_size < 0:
            raise ValueError(f"max_cache_size must be non-negative, got {max_size}")
        self._ttl = ttl_seconds or 0
        self._max_size = max_size if max_size is not None else DEFAULT_MAX_CACHE_SIZE
        self._entries: dict[str, _CacheEntry] = {}
        self._last_cleanup = time.monotonic()

    @property
    def enabled(self) -> bool:
        """Return whether caching is active."""
        return self._ttl > 0 and self._max_size > 0

    # -- public API ----------------------------------------------------------

    def get(self, token: str) -> tuple[bool, AccessToken | None]:
        """Look up a cached verification result.

View on GitHub (pinned to 1f02114297)

Solutions

  1. Set cache_ttl_seconds to a non-negative number (0 or None disables caching)
  2. Fix the config/env value source so it parses to a positive integer
  3. Check any TTL arithmetic (e.g. expiry - now) and clamp with max(0, delta)

Example fix

// before
TokenCache(ttl_seconds=-60)
// after
TokenCache(ttl_seconds=60)  # or 0/None to disable
Defensive patterns

Strategy: validation

Validate before calling

def ensure_valid_cache_config(ttl_seconds=None, max_size=None):
    if ttl_seconds is not None and ttl_seconds < 0:
        raise ValueError(f"cache_ttl_seconds must be non-negative, got {ttl_seconds}")
    if max_size is not None and max_size < 0:
        raise ValueError(f"max_cache_size must be non-negative, got {max_size}")
    return TokenCache(ttl_seconds=ttl_seconds, max_size=max_size)

Type guard

def is_valid_ttl(v) -> bool:
    return v is None or (isinstance(v, (int, float)) and v >= 0)

Try / catch

try:
    cache = TokenCache(ttl_seconds=ttl)
except ValueError as e:
    logger.warning("Bad cache config: %s; disabling cache", e)
    cache = TokenCache(ttl_seconds=None)

Prevention

When it happens

Trigger: Constructing the token cache (directly or via a provider that wires one up) with cache_ttl_seconds set to a negative value, e.g. TokenCache(ttl_seconds=-60) or a config/env value like TTL=-1 parsed into an int.

Common situations: Config files or environment variables holding signed integers where a '-' was typed by mistake; computing a TTL via subtraction (now - later) that went negative; copying a tuning example and flipping the sign.

Related errors


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