tiangolo/fastapi · error · HTTPException
Incorrect username or password
Error message
Incorrect username or password
What it means
Raised by the `/token` login handler with HTTP 400 when `fake_users_db.get(form_data.username)` returns nothing - i.e. the submitted username does not exist. The message deliberately does not distinguish 'no such user' from 'wrong password' to avoid user enumeration.
Source
Thrown at docs_src/security/tutorial003_an_py310.py:81
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"},
)
return user
async def get_current_active_user(
current_user: Annotated[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: Annotated[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: Annotated[User, Depends(get_current_active_user)],
):
return current_user
View on GitHub (pinned to 42a41db11f)
Solutions
- Send credentials as `application/x-www-form-urlencoded` (OAuth2PasswordRequestForm), not JSON.
- Confirm the username exists in the user store (e.g. `johndoe`).
- Check the password too - the same message is reused for wrong password (line 85).
Example fix
# before
curl -X POST http://localhost:8000/token -H 'Content-Type: application/json' \
-d '{"username":"johndoe","password":"secret"}'
# after
curl -X POST http://localhost:8000/token \
-d 'username=johndoe&password=secret' Defensive patterns
Strategy: try-catch
Validate before calling
def is_form_login(username: str, password: str) -> bool:
return bool(username) and bool(password)
# POST with Content-Type: application/x-www-form-urlencoded when True Type guard
def is_credentials_error(resp) -> bool:
return getattr(resp, 'status_code', None) in (400, 401) and \
resp.json().get('detail') == 'Incorrect username or password' Try / catch
try:
tok = client.post('/token', data={'username': u, 'password': p})
tok.raise_for_status()
except httpx.HTTPStatusError as e:
if e.response.json().get('detail') == 'Incorrect username or password':
show_user_friendly_login_error() # do NOT reveal which part was wrong
raise Prevention
- Always POST /token as form-encoded, per the OAuth2 password grant spec.
- Show the user the exact generic message the server returns - do not add detail that enables enumeration.
- Rate-limit /token to blunt brute-force attempts.
When it happens
Trigger: `POST /token` (form-encoded) with `username=nobody` where `nobody` is not a key in `fake_users_db`. The check is `if not user_dict:` at line 80.
Common situations: Typo in the username; client posting JSON instead of OAuth2 form data so `form_data.username` is empty; user not yet provisioned; environment DB missing the seed users.
Related errors
- Incorrect username or password
- Incorrect username or password
- Incorrect username or password
- Incorrect username or password
- Incorrect username or password
AI-assisted analysis of tiangolo/fastapi@42a41db11f (2026-08-04).
Data as JSON: /data/errors/b7bb8d01417dc191.json.
Report an issue: GitHub.