{"record":{"id":"fe0cb9e755969283","repo":"BerriAI/litellm","slug":"byok-auth-required-fe0cb9","errorCode":"byok_auth_required","errorMessage":"User identity is required for BYOK servers","messagePattern":"User identity is required for BYOK servers","errorType":"http","errorClass":"HTTPException","httpStatus":401,"severity":"error","filePath":"litellm/proxy/_experimental/mcp_server/server.py","lineNumber":2554,"sourceCode":"        _write_byok_cred_cache(user_id, mcp_server.server_id, credential)\n        return credential\n\n    async def _check_byok_credential(\n        mcp_server: MCPServer,\n        user_api_key_auth: UserAPIKeyAuth | None,\n    ) -> None:\n        \"\"\"\n        If the MCP server is BYOK-enabled, verify that the requesting user has a\n        stored credential.  When no credential is found, raise an HTTP 401 with a\n        WWW-Authenticate header that points the MCP client to our OAuth metadata\n        endpoint so it can drive the authorization flow.\n        \"\"\"\n        if not mcp_server.is_byok:\n            return\n\n        user_id: Final = (user_api_key_auth.user_id if user_api_key_auth else None) or \"\"\n        if not user_id:\n            raise HTTPException(\n                status_code=401,\n                detail={\n                    \"error\": \"byok_auth_required\",\n                    \"server_id\": mcp_server.server_id,\n                    \"server_name\": mcp_server.server_name or mcp_server.name,\n                    \"message\": \"User identity is required for BYOK servers\",\n                },\n                headers={\"WWW-Authenticate\": 'Bearer resource_metadata=\"/.well-known/oauth-protected-resource\"'},\n            )\n\n        # Check shared credential cache before hitting the DB.\n        cache_key: Final = (user_id, mcp_server.server_id)\n        cached: Final = _byok_cred_cache.get(cache_key)\n        if cached is not None:\n            cached_cred, ts = cached\n            if time.monotonic() - ts < _BYOK_CRED_CACHE_TTL:\n                if cached_cred is None:\n                    raise HTTPException(","sourceCodeStart":2536,"sourceCodeEnd":2572,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/proxy/_experimental/mcp_server/server.py#L2536-L2572","documentation":"A BYOK (bring-your-own-key) MCP server executes each user's tools with that user's own stored credential. Before dispatch, LiteLLM requires a user identity on the request; if user_api_key_auth.user_id is empty, it raises HTTP 401 with error code byok_auth_required and a WWW-Authenticate header pointing at /.well-known/oauth-protected-resource so standards-compliant MCP clients can start the OAuth flow.","triggerScenarios":"Calling a tool on a BYOK server with a service/team virtual key that has no user bound to it; an anonymous session on a public-internet deployment reaching a BYOK tool.","commonSituations":"CI or service-to-service keys created without an owner; org-level keys reused for MCP calls; scripts using raw keys that were never mapped to a user.","solutions":["Attach the key to a user: set user_id when creating the virtual key, or edit the key in the admin UI.","Call with a personal key that already carries a user_id.","If the deployment is intentionally anonymous, do not mark the server is_byok — use a server-level credential instead."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"async def key_has_user(client: httpx.AsyncClient) -> bool:\n    info = (await client.get(f\"{base}/key/info\")).json()\n    return bool(info.get(\"key_info\", {}).get(\"user_id\"))\n\nif not await key_has_user(client):\n    raise PermissionError(\"BYOK servers require a key bound to a user\")","typeGuard":"def is_byok_auth_required_response(resp_json: dict) -> bool:\n    d = resp_json.get(\"detail\", {})\n    return isinstance(d, dict) and d.get(\"error\") == \"byok_auth_required\"","tryCatchPattern":"except httpx.HTTPStatusError as e:\n    if e.response.status_code == 401:\n        d = e.response.json().get(\"detail\", {})\n        if isinstance(d, dict) and d.get(\"error\") == \"byok_auth_required\" and \"User identity\" in str(d.get(\"message\")):\n            # rebind the key to a user; retrying with the same key will not help\n            raise MissingUserIdentity(server=d.get(\"server_id\")) from e\n    raise","preventionTips":["Bind every virtual key that will touch BYOK servers to a user_id at creation time.","Audit service/team keys for missing user_id before enabling BYOK servers."],"tags":["mcp","byok","authentication","http-401"],"backgroundTag":"byok-user-identity-missing","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}