tiangolo/fastapi · error · HTTPException
Incorrect username or password
Error message
Incorrect username or password
What it means
Legacy-DI variant of error 41. The /token handler raises HTTP 400 'Incorrect username or password' when fake_users_db.get(form_data.username) is None. Same anti-enumeration rationale and same message as the password-mismatch branch at line 81: an attacker cannot tell whether the username or the password was wrong.
Solutions
- POST username exactly matching a fake_users_db key plus the matching password.
- Verify the OAuth2PasswordRequestForm field name is 'username' and Content-Type is application/x-www-form-urlencoded.
- Normalize input (strip/lower) before lookup if your domain requires case-insensitive logins.
Defensive patterns
Strategy: validation
Validate before calling
KNOWN_USERS = {"johndoe", "alice"}
def valid_login_form(username: str, password: str) -> bool:
return isinstance(username, str) and username.strip() in KNOWN_USERS and bool(password) Type guard
from typing import TypeGuard
def is_non_empty_creds(pair: tuple) -> TypeGuard[tuple[str, str]]:
u, p = pair
return isinstance(u, str) and isinstance(p, str) and u.strip() != "" and p != "" Try / catch
import httpx
try:
r = httpx.post("/token", data={"username": u, "password": p})
except httpx.HTTPStatusError as e:
if e.response.status_code in (400, 401):
show_generic_login_error() Prevention
- Send 'username' as a form field with the correct Content-Type.
- Normalize whitespace/case if the domain requires it.
- Handle 400 and 401 identically on the client.
When it happens
Trigger: POST /token with username not equal to 'johndoe' or 'alice' (case/whitespace sensitive), or with the field misnamed (e.g. 'user' instead of 'username').
Common situations: Case mismatch, copy-paste whitespace, JSON body instead of form-encoded, stale test fixture.
Related errors
AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11).
Data as JSON: /api/errors/3fc278352c08ca17.
Report an issue: GitHub.
Appendix: source
Thrown at docs_src/security/tutorial003_py310.py:77
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"},
)
return user
async def get_current_active_user(current_user: User = Depends(get_current_user)):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user_dict = fake_users_db.get(form_data.username)
if not user_dict:
raise HTTPException(status_code=400, detail="Incorrect username or password")
user = UserInDB(**user_dict)
hashed_password = fake_hash_password(form_data.password)
if not hashed_password == user.hashed_password:
raise HTTPException(status_code=400, detail="Incorrect username or password")
return {"access_token": user.username, "token_type": "bearer"}
@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_active_user)):
return current_user
View on GitHub (pinned to 3e8d1526d8)