tiangolo/fastapi · error · HTTPException
Not enough permissions
Error message
Not enough permissions
What it means
In the OAuth2 scopes tutorial, `get_current_user` raises HTTP 401 'Not enough permissions' (with `WWW-Authenticate: Bearer scope="..."`) when the token's scopes do not include every scope required by the route's `Security(..., scopes=[...])`. The token itself is valid and the user exists - it just lacks authorization for this endpoint.
Source
Thrown at docs_src/security/tutorial005_an_py310.py:135
detail="Could not validate credentials",
headers={"WWW-Authenticate": authenticate_value},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username = 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: Annotated[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: Annotated[OAuth2PasswordRequestForm, Depends()],View on GitHub (pinned to 42a41db11f)
Solutions
- Re-request a token asking for the required scope, e.g. `scope=items` at /token.
- For multiple endpoints, request all needed scopes: `scope=me items`.
- Confirm the route's required scopes match what the client requests.
Example fix
# before # token requested with scope=me, then: GET /users/me/items/ # requires 'items' => 401 # after # request scope='me items' at /token, 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)
# before calling /users/me/items/, ensure 'items' is in the scopes you requested 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']) # widen scopes, retry once
raise Prevention
- Request the union of scopes every endpoint you call requires.
- Match scope strings exactly (case/whitespace) with the server config.
- On 'Not enough permissions', re-login for broader scopes rather than retrying blindly.
When it happens
Trigger: `GET /users/me/items/` (requires scope `items`) with a token whose `scope` claim is only `me`. The mismatch is detected in the `for scope in security_scopes.scopes:` loop at line 133.
Common situations: Requesting the wrong scopes at /token; frontend assuming a scope it never asked for; route's required scopes changed but client still requests the old set; scope strings differing by case/whitespace.
Related errors
- Not enough permissions
- Incorrect username or password
- Incorrect username or password
- Not authenticated
- Inactive user
AI-assisted analysis of tiangolo/fastapi@42a41db11f (2026-08-04).
Data as JSON: /data/errors/0e5e56ac14b68581.json.
Report an issue: GitHub.