{"record":{"id":"cb8dda61777bb19d","repo":"PrefectHQ/fastmcp","slug":"require-roles-needs-at-least-one-role-a-check-w","errorCode":null,"errorMessage":"require_roles() needs at least one role; a check with no roles would admit any authenticated caller.","messagePattern":"require_roles\\(\\) needs at least one role; a check with no roles would admit any authenticated caller\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/utilities/authorization.py","lineNumber":190,"sourceCode":"\n    Unlike `require_scopes`, this check cannot signal a shortfall: OAuth has no\n    way to request a role, so there is no `insufficient_scope` challenge to\n    emit. A role denial is therefore reported as a plain `AuthorizationError`,\n    and it suppresses any scope shortfall alongside it — a caller blocked by\n    their role must not be told to go obtain a scope that would not help.\n    Scope shortfalls are still reported normally whenever the role check\n    passes.\n\n    Args:\n        *roles: Roles the caller must hold. All are required (AND logic).\n        extract: Callable mapping the token's claims to the caller's roles.\n\n    Raises:\n        ValueError: If no roles are given, which would allow any authenticated\n            caller and is more likely a mistake than an intent.\n    \"\"\"\n    if not roles:\n        raise ValueError(\n            \"require_roles() needs at least one role; a check with no roles \"\n            \"would admit any authenticated caller.\"\n        )\n    return _RequireRoles(roles, extract)\n\n\ndef restrict_tag(tag: str, *, scopes: list[str]) -> AuthCheck:\n    \"\"\"Require scopes when the accessed component has a specific tag.\"\"\"\n    return _RestrictTag(tag, scopes)\n\n\ndef scope_requirements(\n    checks: AuthCheck | list[AuthCheck],\n    ctx: AuthContext,\n) -> list[str] | None:\n    \"\"\"Scopes a check list requires but the token lacks, without running it.\n\n    Returns ``None`` when the list contains any opaque (non-scope) check. Such a","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/utilities/authorization.py#L172-L208","documentation":"require_roles() rejects an empty role list at construction time. A role check with zero roles would trivially pass for any authenticated user, which the API treats as almost certainly a programming mistake, so it raises ValueError instead of creating the guard.","triggerScenarios":"Calling require_roles() or require_roles([]) (or with only keyword defaults and no roles), typically when the role list is computed dynamically from an empty collection.","commonSituations":"Loading allowed roles from config/env that ends up empty; filtering out roles the user lacks before passing them in; typos reading a config key that returns [].","solutions":["Pass at least one explicit role to require_roles","Fix the config/env source so the role list is populated","If the intent is 'any authenticated user', use an authenticated-only check instead of require_roles"],"exampleFix":"// before\nroles = settings.allowed_roles  # []\nguard = require_roles(*roles)\n\n// after\nassert settings.allowed_roles, \"allowed_roles must not be empty\"\nguard = require_roles(*settings.allowed_roles)","handlingStrategy":"validation","validationCode":"roles = settings.allowed_roles or []\nif not roles:\n    raise ValueError(\"configure at least one role before building the guard\")\nguard = require_roles(*roles, extract=extract)","typeGuard":null,"tryCatchPattern":"try:\n    guard = require_roles(*configured_roles)\nexcept ValueError:\n    logger.error(\"allowed_roles is empty; check config\")\n    raise","preventionTips":["Fail fast at config load if role lists are empty","Never pass dynamically filtered role lists directly into require_roles","Use an authenticated-only policy when 'any user' is the real intent"],"tags":["python","auth","authorization","rbac"],"backgroundTag":"empty-authorization-role-list","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}