{"record":{"id":"ffad25c56ee2f10e","repo":"PrefectHQ/fastmcp","slug":"cache-ttl-seconds-must-be-non-negative-got-ttl-s","errorCode":null,"errorMessage":"cache_ttl_seconds must be non-negative, got {ttl_seconds}","messagePattern":"cache_ttl_seconds must be non-negative, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/utilities/token_cache.py","lineNumber":78,"sourceCode":"    \"\"\"\n\n    def __init__(\n        self,\n        *,\n        ttl_seconds: int | None = None,\n        max_size: int | None = None,\n    ) -> None:\n        \"\"\"Initialise the cache.\n\n        Args:\n            ttl_seconds: How long cached entries remain valid, in seconds.\n                ``None`` or ``0`` disables caching entirely.\n            max_size: Upper bound on the number of entries.  When the limit is\n                reached, expired entries are swept first; if still full the\n                oldest entry is evicted.  Defaults to 10 000.\n        \"\"\"\n        if ttl_seconds is not None and ttl_seconds < 0:\n            raise ValueError(\n                f\"cache_ttl_seconds must be non-negative, got {ttl_seconds}\"\n            )\n        if max_size is not None and max_size < 0:\n            raise ValueError(f\"max_cache_size must be non-negative, got {max_size}\")\n        self._ttl = ttl_seconds or 0\n        self._max_size = max_size if max_size is not None else DEFAULT_MAX_CACHE_SIZE\n        self._entries: dict[str, _CacheEntry] = {}\n        self._last_cleanup = time.monotonic()\n\n    @property\n    def enabled(self) -> bool:\n        \"\"\"Return whether caching is active.\"\"\"\n        return self._ttl > 0 and self._max_size > 0\n\n    # -- public API ----------------------------------------------------------\n\n    def get(self, token: str) -> tuple[bool, AccessToken | None]:\n        \"\"\"Look up a cached verification result.","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/utilities/token_cache.py#L60-L96","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Set cache_ttl_seconds to a non-negative number (0 or None disables caching)","Fix the config/env value source so it parses to a positive integer","Check any TTL arithmetic (e.g. expiry - now) and clamp with max(0, delta)"],"exampleFix":"// before\nTokenCache(ttl_seconds=-60)\n// after\nTokenCache(ttl_seconds=60)  # or 0/None to disable","handlingStrategy":"validation","validationCode":"def ensure_valid_cache_config(ttl_seconds=None, max_size=None):\n    if ttl_seconds is not None and ttl_seconds < 0:\n        raise ValueError(f\"cache_ttl_seconds must be non-negative, got {ttl_seconds}\")\n    if max_size is not None and max_size < 0:\n        raise ValueError(f\"max_cache_size must be non-negative, got {max_size}\")\n    return TokenCache(ttl_seconds=ttl_seconds, max_size=max_size)","typeGuard":"def is_valid_ttl(v) -> bool:\n    return v is None or (isinstance(v, (int, float)) and v >= 0)","tryCatchPattern":"try:\n    cache = TokenCache(ttl_seconds=ttl)\nexcept ValueError as e:\n    logger.warning(\"Bad cache config: %s; disabling cache\", e)\n    cache = TokenCache(ttl_seconds=None)","preventionTips":["Validate config values at load time, before they reach the library","Use None (not a negative sentinel) to express 'unlimited/disabled'","Clamp computed TTLs: ttl = max(0, expiry - now)","Add a unit test asserting negative inputs raise ValueError"],"tags":["validation","configuration","valueerror"],"backgroundTag":"invalid-parameter-value","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}