tiangolo/fastapi · error · HTTPException

Incorrect username or password

Error message

Incorrect username or password

What it means

Default-parameter twin of error 44: the `/token` handler raises HTTP 400 'Incorrect username or password' when `fake_users_db.get(form_data.username)` is None. Message is shared with the wrong-password branch (line 81) to prevent user enumeration.

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 42a41db11f)

Solutions

  1. POST as `application/x-www-form-urlencoded` per the OAuth2 password grant.
  2. Use a username that exists in the store.
  3. Remember the same message covers a wrong password (line 81).

Example fix

# before
curl -X POST http://localhost:8000/token -d 'username=ghost&password=x'
# 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()
    raise

Prevention

When it happens

Trigger: `POST /token` form-encoded with `username=<unknown>`. Branch `if not user_dict:` at line 76.

Common situations: Username typo; JSON body sent instead of form data so `form_data.username` is empty; user not provisioned; wrong environment.

Related errors


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