BerriAI/litellm · error · HTTPException

User ID not found in token

Error message

User ID not found in token

What it means

Returned (400) by the store-BYOK-credential endpoint when user_api_key_dict.user_id is empty after the server is authorized. The endpoint needs a concrete user to scope the credential to (store_user_credential(prisma_client, user_id, server_id, ...)), so a key that authenticates but is not bound to a user — typically the master key or a bare virtual key — cannot store per-user credentials.

Source

Thrown at litellm/proxy/management_endpoints/mcp_management_endpoints.py:2054

        response_model=MCPUserCredentialResponse,
    )
    @management_endpoint_wrapper
    async def store_mcp_user_credential(
        server_id: str,
        payload: MCPUserCredentialRequest,
        user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
    ):
        """Store a BYOK credential for the calling user."""
        prisma_client: Final = get_prisma_client_or_throw("Database not connected. Connect a database to your proxy")
        mcp_server: Final = await _authorize_and_fetch_mcp_server(prisma_client, user_api_key_dict, server_id)
        if not getattr(mcp_server, "is_byok", False):
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail={"error": "This MCP server does not support BYOK credentials"},
            )
        user_id: Final = user_api_key_dict.user_id or ""
        if not user_id:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail={"error": "User ID not found in token"},
            )
        if payload.save:
            await store_user_credential(prisma_client, user_id, server_id, payload.credential)
            from litellm.proxy._experimental.mcp_server.server import (
                _invalidate_byok_cred_cache,
            )

            _invalidate_byok_cred_cache(user_id, server_id)
            return MCPUserCredentialResponse(server_id=server_id, has_credential=True)
        # save=False: credential not persisted
        return MCPUserCredentialResponse(server_id=server_id, has_credential=False)

    @router.delete(
        "/server/{server_id}/user-credential",
        description="Delete the calling user's stored API key for a BYOK MCP server",
        dependencies=[Depends(user_api_key_auth)],

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Use a key that is bound to a user (create the virtual key with a user / user_id) and retry.
  2. Check the calling key first via /key/info and confirm its user_id is set.
  3. For UI flows, log in as the user so the session token carries the user binding.

Example fix

# before
requests.post(f"{PROXY}/v1/mcp/server/{server_id}/credentials", headers={"Authorization": f"Bearer {os.environ['LITELLM_MASTER_KEY']}"}, json=payload)

# after: use a user-bound virtual key
requests.post(f"{PROXY}/v1/mcp/server/{server_id}/credentials", headers={"Authorization": f"Bearer {USER_VIRTUAL_KEY}"}, json=payload)
Defensive patterns

Strategy: validation

Validate before calling

info = requests.get(f"{PROXY}/key/info", headers=AUTH, params={"key": KEY}).json()
user_id = info.get("key_info", info).get("user_id")
if not user_id:
    raise ValueError("use a virtual key created with user_id for BYOK credential storage")

Type guard

def key_bound_to_user(key_info: dict) -> bool:
    return bool(key_info.get("user_id"))

Try / catch

try:
    store_credential(server_id, payload)
except HTTPError as e:
    if e.response.status_code == 400 and "User ID not found" in e.response.text:
        raise ValueError("switch to the user's own virtual key (it must carry user_id)")
    raise

Prevention

When it happens

Trigger: Calling the credential store endpoint with the proxy master key; using a virtual key created without a user binding; service-to-service keys that have role but no user_id.

Common situations: Scripts authenticating with LITELLM_MASTER_KEY for everything; keys generated before user assignment policies existed; migrating from shared keys to per-user BYOK without re-issuing keys.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/aee0a65d85636d43. Report an issue: GitHub.