tiangolo/fastapi · error · HTTPException

Not enough permissions

Error message

Not enough permissions

What it means

Default-parameter twin of error 54: `get_current_user` raises HTTP 401 'Not enough permissions' (header `Bearer scope="..."`) when the token's scopes do not cover the route's required scopes (loop at line 132). The JWT and user are valid; only the authorization scope is missing.

Source

Thrown at docs_src/security/tutorial005_py310.py:134

        detail="Could not validate credentials",
        headers={"WWW-Authenticate": authenticate_value},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        scope: str = payload.get("scope", "")
        token_scopes = scope.split(" ")
        token_data = TokenData(scopes=token_scopes, username=username)
    except (InvalidTokenError, ValidationError):
        raise credentials_exception
    user = get_user(fake_users_db, username=token_data.username)
    if user is None:
        raise credentials_exception
    for scope in security_scopes.scopes:
        if scope not in token_data.scopes:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail="Not enough permissions",
                headers={"WWW-Authenticate": authenticate_value},
            )
    return user


async def get_current_active_user(
    current_user: User = Security(get_current_user, scopes=["me"]),
):
    if current_user.disabled:
        raise HTTPException(status_code=400, detail="Inactive user")
    return current_user


@app.post("/token")
async def login_for_access_token(
    form_data: OAuth2PasswordRequestForm = Depends(),

View on GitHub (pinned to 42a41db11f)

Solutions

  1. Re-request a token with the required scope(s): `scope=items` or `scope=me items`.
  2. Align the client's requested scopes with every route's `Security(..., scopes=...)`.
  3. Recheck scope strings for exact case/whitespace.

Example fix

# before
# token scope=me; calling an 'items' route => 401
# after
# re-login with scope='me items', then retry
Defensive patterns

Strategy: validation

Validate before calling

def has_scopes(token_scopes, required) -> bool:
    return all(s in token_scopes for s in required)
# ensure 'items' (or whichever the route needs) is in token_scopes before calling

Type guard

def has_required_scope(token_scopes: list[str], required: list[str]) -> bool:
    return set(required).issubset(set(token_scopes))

Try / catch

try:
    r = client.get('/users/me/items/', headers=auth_header(token))
    r.raise_for_status()
except httpx.HTTPStatusError as e:
    if e.response.status_code == 401 and e.response.json().get('detail') == 'Not enough permissions':
        token = request_token(scopes=['me','items'])
    raise

Prevention

When it happens

Trigger: `GET /users/me/items/` (requires `items`) with a token whose `scope` claim lacks `items` (e.g. only `me`).

Common situations: Wrong scopes requested at /token; client assuming a scope it never obtained; required-scopes changed server-side; case/whitespace mismatch in scope strings.

Related errors


AI-assisted analysis of tiangolo/fastapi@42a41db11f (2026-08-04). Data as JSON: /data/errors/80a392c0326bc71a.json. Report an issue: GitHub.