{"record":{"id":"0c662e22f1c0f27b","repo":"BerriAI/litellm","slug":"mcpjwtsigner-ttl-seconds-must-be-0-got-resolv","errorCode":null,"errorMessage":"MCPJWTSigner: ttl_seconds must be > 0, got {resolved_ttl}","messagePattern":"MCPJWTSigner: ttl_seconds must be > 0, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/proxy/guardrails/guardrail_hooks/mcp_jwt_signer/mcp_jwt_signer.py","lineNumber":296,"sourceCode":"            self._persistent_key = False\n            verbose_proxy_logger.info(\n                \"MCPJWTSigner: auto-generated RSA-2048 keypair (set %s to use your own key)\",\n                self.SIGNING_KEY_ENV,\n            )\n\n        self._public_key = self._private_key.public_key()\n        self._kid = _compute_kid(self._public_key)\n\n        # --- Core config ---\n        self.issuer: str = (\n            issuer or os.environ.get(\"MCP_JWT_ISSUER\") or os.environ.get(\"LITELLM_EXTERNAL_URL\") or \"litellm\"\n        )\n        self.audience: str = audience or os.environ.get(\"MCP_JWT_AUDIENCE\") or self.DEFAULT_AUDIENCE\n        resolved_ttl: Final = int(\n            ttl_seconds if ttl_seconds is not None else os.environ.get(\"MCP_JWT_TTL_SECONDS\", str(self.DEFAULT_TTL))\n        )\n        if resolved_ttl <= 0:\n            raise ValueError(f\"MCPJWTSigner: ttl_seconds must be > 0, got {resolved_ttl}\")\n        self.ttl_seconds: int = resolved_ttl\n\n        # --- FR-5: Verify + re-sign ---\n        self.access_token_discovery_uri: str | None = access_token_discovery_uri\n        self.token_introspection_endpoint: str | None = token_introspection_endpoint\n        self.verify_issuer: str | None = verify_issuer\n        self.verify_audience: str | None = verify_audience\n        # Cached OIDC discovery document (fetched lazily, TTL = 24 h)\n        self._oidc_discovery_doc: _OIDCDiscoveryDocument | None = None\n        self._oidc_discovery_fetched_at: float = 0.0\n\n        # --- FR-12: End-user identity mapping ---\n        # Default chain: try incoming JWT sub, fall back to litellm user_id\n        self.end_user_claim_sources: list[str] = end_user_claim_sources or [\n            \"token:sub\",\n            \"litellm:user_id\",\n        ]\n","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/guardrails/guardrail_hooks/mcp_jwt_signer/mcp_jwt_signer.py#L278-L314","documentation":"MCPJWTSigner resolves its JWT lifetime from the ttl_seconds constructor argument or the MCP_JWT_TTL_SECONDS environment variable (defaulting to DEFAULT_TTL), then requires the value to be a positive integer. A zero or negative resolved value raises ValueError at construction, i.e. at proxy startup.","triggerScenarios":"MCP_JWT_TTL_SECONDS=0 (or a negative number) in the proxy environment, or ttl_seconds <= 0 passed via guardrail litellm_params. A non-numeric value fails earlier inside int().","commonSituations":"'Disable expiry' attempts by setting TTL to 0; copy-paste from examples with placeholder values; templating that renders an empty or zero default; unit tests constructing the signer with ttl_seconds=0.","solutions":["Set MCP_JWT_TTL_SECONDS (or ttl_seconds) to a positive number of seconds, e.g. 3600","If you want long-lived tokens, use a large TTL rather than 0 - zero is rejected by design","Remove the variable to fall back to the signer's DEFAULT_TTL"],"exampleFix":"# before - zero TTL rejected\nlitellm_params:\n  ttl_seconds: 0\n\n# after - one-hour tokens\nlitellm_params:\n  ttl_seconds: 3600","handlingStrategy":"validation","validationCode":"import os  \n  \nttl = os.environ.get(\"MCP_JWT_TTL_SECONDS\")  \nif ttl is not None:  \n    ttl_i = int(ttl)  \n    assert ttl_i > 0, f\"MCP_JWT_TTL_SECONDS must be > 0, got {ttl_i}\"","typeGuard":"def is_positive_int(v: object) -> bool:  \n    return isinstance(v, int) and not isinstance(v, bool) and v > 0","tryCatchPattern":null,"preventionTips":["Never use 0 to try to disable expiry - pick a real lifetime (e.g. 300-3600s)","Omit ttl_seconds/MCP_JWT_TTL_SECONDS to inherit the signer default"],"tags":["mcp","jwt","guardrail","config","startup"],"backgroundTag":"config-validation-failed","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}