{"record":{"id":"643352a3c16108b9","repo":"PrefectHQ/fastmcp","slug":"cache-ttl-must-be-a-positive-integer-got-cache-t","errorCode":null,"errorMessage":"cache_ttl must be a positive integer, got {cache_ttl}","messagePattern":"cache_ttl must be a positive integer, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/caching.py","lineNumber":56,"sourceCode":"    `cache_ttl` is meaningless (the client gates caching on the presence of a\n    TTL) and is rejected rather than silently ignored.\n\n    Returns `None` when no hint is set, or a map applying the same hint to every\n    SDK-cacheable method otherwise.\n\n    Raises:\n        ValueError: If `cache_ttl` is not positive, or if `cache_scope` is set\n            without `cache_ttl`.\n    \"\"\"\n    if cache_ttl is None:\n        if cache_scope is not None:\n            raise ValueError(\n                \"cache_scope requires cache_ttl; a scope without a TTL does not \"\n                \"enable caching\"\n            )\n        return None\n    if cache_ttl <= 0:\n        raise ValueError(f\"cache_ttl must be a positive integer, got {cache_ttl}\")\n    hint = CacheHint(ttl_ms=cache_ttl * 1000, scope=cache_scope or \"private\")\n    return dict.fromkeys(get_args(CacheableMethod), hint)\n","sourceCodeStart":38,"sourceCodeEnd":59,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/caching.py#L38-L59","documentation":"build_cache_hints validates the FastMCP constructor's cache_ttl argument (seconds) used to emit SEP-2549 client-side cache hints. A non-positive TTL (zero or negative) cannot produce a meaningful cache lifetime, so the server refuses to start rather than silently emitting an inert or invalid hint. The TTL is converted to milliseconds for the wire (ttl_ms = cache_ttl * 1000).","triggerScenarios":"Passing cache_ttl=0, cache_ttl<0, or a non-integer (e.g. 1.5) to FastMCP(..., cache_ttl=...). The check runs in build_cache_hints, called from FastMCP.__init__, so any construction with such a value raises immediately.","commonSituations":"Copy-pasted config where the TTL was left at a placeholder 0; unit tests probing invalid input; config-driven servers where a YAML/env value of 0 or -1 means 'disabled' to the author but is passed straight through as a number instead of None.","solutions":["Pass a positive integer for cache_ttl (seconds), e.g. cache_ttl=60.","To disable caching, omit cache_ttl entirely (pass None), not 0.","If the value comes from config/env, coerce and validate before constructing FastMCP: ttl = int(raw) if raw else None.","Note cache_scope cannot be given without cache_ttl; set both together or neither."],"exampleFix":"// before\nmcp = FastMCP(\"srv\", cache_ttl=0, cache_scope=\"private\")\n// after\nmcp = FastMCP(\"srv\", cache_ttl=300, cache_scope=\"private\")\n# or disable: mcp = FastMCP(\"srv\")","handlingStrategy":"validation","validationCode":"if cache_ttl is not None and (not isinstance(cache_ttl, int) or cache_ttl <= 0):\n    raise ValueError(f\"cache_ttl must be a positive integer, got {cache_ttl!r}\")","typeGuard":null,"tryCatchPattern":"try:\n    mcp = FastMCP(\"srv\", cache_ttl=cfg.ttl, cache_scope=cfg.scope)\nexcept ValueError as e:\n    logging.error(\"bad cache config: %s\", e)\n    mcp = FastMCP(\"srv\")  # caching disabled","preventionTips":["Treat 0/\"\" from config as 'unset' and map to None before constructing FastMCP.","Validate TTL-bearing config at startup with a schema (pydantic) using PositiveInt | None.","Remember cache_scope requires cache_ttl; validate them as a pair."],"tags":["python","configuration","caching","valueerror"],"backgroundTag":"invalid-configuration-value","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}