PrefectHQ/fastmcp · error · ValueError

max_cache_size must be non-negative, got {max_size}

Error message

max_cache_size must be non-negative, got {max_size}

What it means

The TokenCache constructor validates its max_size parameter and raises ValueError when a negative number is passed. A negative capacity cannot bound the cache, so the library rejects it eagerly. Omit the parameter to use the default (10,000 entries) or pass None explicitly.

Source

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

        *,
        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.

        Returns:
            ``(True, AccessToken)`` on a cache hit, ``(False, None)`` on a miss
            or when caching is disabled.  The returned ``AccessToken`` is a deep

View on GitHub (pinned to 1f02114297)

Solutions

  1. Set max_size to a non-negative integer
  2. Pass None (or omit) to use the default max cache size
  3. Fix the config/env source producing the negative number

Example fix

// before
TokenCache(max_size=-1000)
// after
TokenCache(max_size=1000)  # or max_size=None for default
Defensive patterns

Strategy: validation

Validate before calling

def safe_token_cache(max_size=None, ttl_seconds=None):
    if max_size is not None and max_size < 0:
        max_size = None  # fall back to library default
    return TokenCache(ttl_seconds=ttl_seconds, max_size=max_size)

Type guard

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

Try / catch

try:
    cache = TokenCache(max_size=size)
except ValueError as e:
    logger.warning("Bad max_size: %s; using default", e)
    cache = TokenCache(max_size=None)

Prevention

When it happens

Trigger: Constructing the token cache with max_size < 0, e.g. TokenCache(max_size=-1), or supplying a signed config value that parses to a negative int.

Common situations: Config typo ('-1000' instead of '1000'); unbounded growth attempts using a sentinel negative value instead of None; arithmetic on sizes that underflowed.

Related errors


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