tiangolo/fastapi · error · HTTPException
Not enough permissions
Error message
Not enough permissions
What it means
Legacy-DI variant of error 51. get_current_user iterates the route's required SecurityScopes against the token's space-delimited 'scope' claim and raises HTTP 401 'Not enough permissions' with WWW-Authenticate: Bearer scope="<required>" when a required scope is missing. Semantics identical to the Annotated version; only the DI syntax differs.
Solutions
- Request the required scope at POST /token via the form field scope (e.g. scope='me items').
- Re-login to obtain a token containing the needed scope.
- Confirm scope names match those declared on OAuth2PasswordBearer.
Defensive patterns
Strategy: validation
Validate before calling
import jwt
REQUIRED = {"items"}
def has_required_scopes(token: str, required: set[str]) -> bool:
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
token_scopes = set(payload.get("scope", "").split())
return required.issubset(token_scopes) Type guard
from typing import TypeGuard
def token_has_scopes(token: str, need: set[str]) -> TypeGuard[str]:
import jwt
p = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
return need.issubset(set(p.get("scope", "").split())) Try / catch
import httpx
try:
r = httpx.get("/users/me/items/", headers={"Authorization": f"Bearer {token}"})
r.raise_for_status()
except httpx.HTTPStatusError as e:
if e.response.status_code == 401:
need = parse_required_scopes(e.response.headers.get("www-authenticate", ""))
token = login_with_scopes(need) Prevention
- Request all required scopes in the 'scope' form field at /token.
- Match scope names to those declared on OAuth2PasswordBearer.
- Decode the JWT locally to fail fast on missing scopes.
When it happens
Trigger: Call a route guarded by Security(get_current_user, scopes=['items']) (e.g. GET /users/me/items/) with a token whose scope claim lacks 'items' (e.g. scope=me only).
Common situations: Wrong scopes requested at /token; scope naming mismatch; token minted before scopes existed.
Related errors
AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11).
Data as JSON: /api/errors/80a392c0326bc71a.
Report an issue: GitHub.
Appendix: 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 3e8d1526d8)