BerriAI/litellm · error · Exception

No team found in token. Checked team_id field '{team_id_fiel

Error message

No team found in token. Checked team_id field '{team_id_field}' and team_alias field '{team_alias_field}'.{hint}

What it means

JWT auth tried to resolve a team from the token using `team_id_jwt_field` (and `team_alias_jwt_field`) and found nothing usable in either. The trailing hint (when present) explains a known pitfall: get_nested_value does not support dot-index ('roles.0') or bracket ('roles[0]') access — if the claim is a list, configure the bare field name and LiteLLM takes the first element.

Source

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

                    parts: Final = team_id_field.rsplit(".", 1)
                    if parts[-1].isdigit():
                        base_field = parts[0]
                        hint = (
                            f" Hint: dot-notation array indexing (e.g. '{team_id_field}') is not "
                            f"supported. Use '{base_field}' instead — LiteLLM automatically "
                            f"uses the first element when the field value is a list."
                        )
                # "roles[0]" — bracket-notation indexing is also not supported in get_nested_value
                elif "[" in team_id_field and team_id_field.endswith("]"):
                    m: Final = re.match(r"^(\w+)\[(\d+)\]$", team_id_field)
                    if m:
                        base_field = m.group(1)
                        hint = (
                            f" Hint: array indexing (e.g. '{team_id_field}') is not supported "
                            f"in team_id_jwt_field. Use '{base_field}' instead — LiteLLM "
                            f"automatically uses the first element when the field value is a list."
                        )
            raise Exception(
                f"No team found in token. Checked team_id field '{team_id_field}' and team_alias field '{team_alias_field}'.{hint}"
            )

        return individual_team_id, team_object

    @staticmethod
    def get_all_team_ids(jwt_handler: JWTHandler, jwt_valid_token: dict) -> set[str]:
        """Get combined team IDs from groups and individual team_id"""
        team_ids_from_groups: Final = jwt_handler.get_team_ids_from_jwt(token=jwt_valid_token)

        all_team_ids: Final = set(team_ids_from_groups)

        return all_team_ids

    @staticmethod
    def _team_has_passthrough_route_access(
        team_object: LiteLLM_TeamTable | None,
        route: str,

View on GitHub (pinned to 77b7c6c40c)

Solutions

  1. Decode the JWT and find the actual claim holding the team/group id; set `team_id_jwt_field` to that exact name (dot-paths like 'tenant.team_id' are supported).
  2. If the claim is a list (e.g. Azure 'roles'), set the bare field name ('roles') — LiteLLM uses the first element; do NOT use 'roles[0]' or 'roles.0'.
  3. If the token simply has no team claim, either remove team_id_jwt_field, use `team_id_default`, or enable fallback mechanisms.
  4. For alias-based lookup, verify `team_alias_jwt_field` matches the claim carrying the team alias in the DB.

Example fix

# config.yaml — before (Azure AD token with roles: ["team-123"])
litellm_jwtauth:
  team_id_jwt_field: "roles[0]"

# after
litellm_jwtauth:
  team_id_jwt_field: "roles"
Defensive patterns

Strategy: validation

Validate before calling

claims = jwt_claims(token)
field = CONFIG['litellm_jwtauth']['team_id_jwt_field']
val = claims.get(field) if '.' not in field else nested_get(claims, field)
if val is None or val == []:
    raise ValueError(f'token lacks team claim {field!r}; fix team_id_jwt_field or IdP token')
if isinstance(val, list) and not val:
    raise ValueError('team claim is an empty list')

Type guard

def has_team_claim(claims: dict, field: str) -> bool:
    v = claims
    for part in field.split('.'):
        if not isinstance(v, dict) or part not in v:
            return False
        v = v[part]
    return bool(v) or isinstance(v, list) and len(v) > 0

Try / catch

try:
    team_id, team_obj = JWTAuthManager.get_team_object(claims, ...)
except Exception as e:
    if 'No team found in token' in str(e):
        # hint in message explains list/bracket pitfalls — surface it to the operator
        raise ConfigError(str(e)) from e
    raise

Prevention

When it happens

Trigger: `team_id_jwt_field` set to a claim that doesn't exist in the token (e.g. 'client_id' for Azure AD tokens that use 'roles' or 'tid'). Field configured as 'roles[0]' or 'groups.0'. Claim exists but is null/empty. Nested path like 'tenant.team_id' where the parent key differs.

Common situations: Switching IdPs (Okta -> Azure AD) without updating team_id_jwt_field. Azure AD tokens where team info lives in the array-valued 'roles' or 'groups' claim — developers naturally write 'roles[0]' which is unsupported. Testing with tokens generated by jwt.io without the expected claims.

Related errors


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