{"record":{"id":"314f95606a872668","repo":"PrefectHQ/fastmcp","slug":"jwt-issuer-not-initialized-ensure-get-routes-is","errorCode":null,"errorMessage":"JWT issuer not initialized. Ensure get_routes() is called before token operations.","messagePattern":"JWT issuer not initialized\\. Ensure get_routes\\(\\) is called before token operations\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/server/auth/oauth_proxy/proxy.py","lineNumber":799,"sourceCode":"        # claim is the authorization server's issuer identifier (`issuer_url`),\n        # which matches the `issuer` advertised in the metadata document.\n        self._jwt_issuer = JWTIssuer(\n            issuer=str(self.issuer_url),\n            audience=str(self._resource_url),\n            signing_key=self._jwt_signing_key,\n        )\n\n        logger.debug(\"Configured OAuth proxy for resource URL: %s\", self._resource_url)\n\n    @property\n    def jwt_issuer(self) -> JWTIssuer:\n        \"\"\"Get the JWT issuer, ensuring it has been initialized.\n\n        The JWT issuer is created when set_mcp_path() is called (via get_routes()).\n        This property ensures a clear error if used before initialization.\n        \"\"\"\n        if self._jwt_issuer is None:\n            raise RuntimeError(\n                \"JWT issuer not initialized. Ensure get_routes() is called \"\n                \"before token operations.\"\n            )\n        return self._jwt_issuer\n\n    @property\n    def token_endpoint_url(self) -> str:\n        \"\"\"The token endpoint URL, as advertised in the authorization server metadata.\n\n        A CIMD `private_key_jwt` assertion is bound to this URL as its `aud`, so\n        it must match the advertised `token_endpoint` byte-for-byte. The SDK's\n        `build_metadata` builds that URL by stripping any trailing slash from\n        `base_url` first, so this does too: pydantic renders a bare-authority\n        `base_url` with a trailing slash, which would otherwise expect an `aud`\n        of `https://example.com//token`.\n        \"\"\"\n        return f\"{str(self.base_url).rstrip('/')}/token\"\n","sourceCodeStart":781,"sourceCodeEnd":817,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/server/auth/oauth_proxy/proxy.py#L781-L817","documentation":"The OAuthProxy's jwt_issuer is created lazily when set_mcp_path() runs during get_routes(). Accessing the jwt_issuer property before the auth routes have been wired up raises RuntimeError, since token operations cannot work without an initialized issuer.","triggerScenarios":"Accessing proxy.jwt_issuer (or calling token operation paths that use it) after constructing OAuthProxy but before get_routes() is called by the server setup.","commonSituations":"Writing custom token-verification or introspection code that grabs jwt_issuer directly at import/startup time; unit tests instantiating the proxy in isolation without mounting routes; composing custom auth middleware outside the normal FastMCP server bootstrap.","solutions":["Ensure get_routes() (which triggers set_mcp_path()) is called before any token operation — normally done automatically when mounting the proxy into FastMCP","Reorder custom code so jwt_issuer is accessed only after the server/routes are initialized","In tests, explicitly call set_mcp_path() (or get_routes()) on the proxy before touching jwt_issuer"],"exampleFix":"// before\nproxy = OAuthProxy(...)\nissuer = proxy.jwt_issuer  # RuntimeError\n// after\nproxy = OAuthProxy(...)\nroutes = proxy.get_routes(mcp_path=\"/mcp\")\nissuer = proxy.jwt_issuer  # OK","handlingStrategy":"try-catch","validationCode":"if proxy._jwt_issuer is None:  # or check before server start\n    proxy.set_mcp_path(mcp_path)  # triggers issuer creation\nissuer = proxy.jwt_issuer","typeGuard":"def issuer_ready(proxy) -> bool:\n    return proxy._jwt_issuer is not None","tryCatchPattern":"try:\n    issuer = proxy.jwt_issuer\nexcept RuntimeError as e:\n    if \"JWT issuer not initialized\" in str(e):\n        proxy.get_routes(mcp_path=\"/mcp\")\n        issuer = proxy.jwt_issuer\n    else:\n        raise","preventionTips":["Only access jwt_issuer after mounting the proxy into the FastMCP server (get_routes runs automatically)","In tests, call set_mcp_path() in a fixture before touching token operations","Avoid grabbing internal auth objects at import time"],"tags":["oauth","jwt","lifecycle","initialization-order"],"backgroundTag":"not-initialized-before-use","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}