BerriAI/litellm · error · Exception

Unmapped scope type - {type(token['scope'])}. Supported type

Error message

Unmapped scope type - {type(token['scope'])}. Supported types - list, str.

What it means

Raised in JWTAuthManager.get_scopes (litellm/proxy/auth/handle_jwt.py) while extracting the scope claim from a decoded JWT. LiteLLM accepts scope as either a space-separated string (the OAuth standard) or a list of strings; any other JSON type (number, object, boolean, null-as-present) hits the else branch and raises this generic Exception, failing authentication.

Source

Thrown at litellm/proxy/auth/handle_jwt.py:599

                    key_path=self.litellm_jwtauth.org_alias_jwt_field,
                    default=default_value,
                )
                return org_alias
            else:
                org_alias = None
        except KeyError:
            org_alias = default_value
        return org_alias

    def get_scopes(self, token: dict) -> list[str]:
        try:
            if isinstance(token["scope"], str):
                # Assuming the scopes are stored in 'scope' claim and are space-separated
                scopes = token["scope"].split()
            elif isinstance(token["scope"], list):
                scopes = token["scope"]
            else:
                raise Exception(f"Unmapped scope type - {type(token['scope'])}. Supported types - list, str.")
        except KeyError:
            scopes = []
        return scopes

    async def _resolve_jwks_url(self, url: str) -> str:
        """
        If url points to an OIDC discovery document (*.well-known/openid-configuration),
        fetch it and return the jwks_uri contained within.  Otherwise return url unchanged.
        This lets JWT_PUBLIC_KEY_URL be set to a well-known discovery endpoint instead of
        requiring operators to manually find the JWKS URL.
        """
        if ".well-known/openid-configuration" not in url:
            return url

        cache_key: Final = f"litellm_oidc_discovery_{url}"
        cached_jwks_uri: Final = await self.user_api_key_cache.async_get_cache(cache_key)
        if cached_jwks_uri is not None:
            return cached_jwks_uri

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Fix the token issuer so the scope claim is a space-separated string ("read write") or an array of strings (["read", "write"])
  2. Decode the incoming token (e.g. at jwt.io) and inspect the scope claim's JSON type to confirm the mismatch
  3. If you control neither side, mint the scope correctly in a pre-auth step or ask the IdP admin to fix the claim mapping

Example fix

// before: token payload with unmapped scope type
{ "sub": "user", "scope": { "models": "read" } }

// after: standard space-separated string (or array)
{ "sub": "user", "scope": "models:read chat:completion" }
Defensive patterns

Strategy: type-guard

Validate before calling

import jwt as pyjwt

def token_scope_is_supported(token: str) -> bool:
    payload = pyjwt.decode(token, options={"verify_signature": False})
    scope = payload.get("scope")
    return scope is None or isinstance(scope, (str, list))

Type guard

from typing import Union

def is_supported_scope(scope: object) -> bool:
    """LiteLLM accepts space-separated str or list[str]; anything else raises at auth time."""
    if isinstance(scope, str):
        return len(scope.split()) > 0 or scope == ""
    if isinstance(scope, list):
        return all(isinstance(s, str) for s in scope)
    return False

Prevention

When it happens

Trigger: A request authenticated with a JWT whose scope claim is neither a string nor a list - for example {"scope": 123}, {"scope": {"read": true}}, or {"scope": true} - and the proxy config uses scope-based auth (e.g. scope_endpoints, scope mappings).

Common situations: A custom/internal token minter that encodes scopes as a JSON object or integer; a misconfigured IdP custom claim rule; testing with hand-crafted tokens where scope was written as a non-standard type.

Related errors


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