tiangolo/fastapi · error · HTTPException
Incorrect username or password
Error message
Incorrect username or password
What it means
Legacy-DI variant of error 57. get_current_username compares username/password with secrets.compare_digest and raises HTTP 401 'Incorrect username or password' with WWW-Authenticate: Basic when either fails. The combined AND prevents timing short-circuit. Semantics identical to the Annotated version; only the DI syntax differs.
Source
Thrown at docs_src/security/tutorial007_py310.py:23
app = FastAPI()
security = HTTPBasic()
def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
current_username_bytes = credentials.username.encode("utf8")
correct_username_bytes = b"stanleyjobson"
is_correct_username = secrets.compare_digest(
current_username_bytes, correct_username_bytes
)
current_password_bytes = credentials.password.encode("utf8")
correct_password_bytes = b"swordfish"
is_correct_password = secrets.compare_digest(
current_password_bytes, correct_password_bytes
)
if not (is_correct_username and is_correct_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Basic"},
)
return credentials.username
@app.get("/users/me")
def read_current_user(username: str = Depends(get_current_username)):
return {"username": username}
View on GitHub (pinned to 3e8d1526d8)
Solutions
- Send Authorization: Basic <base64('stanleyjobson:swordfish')>.
- Clear cached Basic credentials in the browser and re-authenticate.
- Externalize credentials to env/secrets instead of hardcoding.
Defensive patterns
Strategy: validation
Validate before calling
import base64, secrets
USER, PW = b"stanleyjobson", b"swordfish"
def basic_header(username: str, password: str) -> str | None:
if not (secrets.compare_digest(username.encode(), USER)
and secrets.compare_digest(password.encode(), PW)):
return None
token = base64.b64encode(f"{username}:{password}".encode()).decode()
return f"Basic {token}" Type guard
from typing import TypeGuard
def is_valid_basic_pair(pair: tuple) -> TypeGuard[tuple[str, str]]:
u, p = pair
return isinstance(u, str) and isinstance(p, str) and u and p Try / catch
import httpx
try:
r = httpx.get("/users/me", headers={"Authorization": basic_header(u, p)})
r.raise_for_status()
except httpx.HTTPStatusError as e:
if e.response.status_code == 401:
prompt_credentials() Prevention
- Encode the Basic header correctly with base64.
- Clear cached browser Basic creds after a change.
- Externalize credentials to env/secrets.
When it happens
Trigger: GET /users/me with Basic credentials whose username is not 'stanleyjobson' or whose password is not 'swordfish' (or with no Authorization header).
Common situations: Credential typo; cached browser creds; mis-encoded test header; rotated demo creds.
Related errors
- Incorrect username or password
- Inactive user
- Not enough permissions
- Invalid X-Token header
- Invalid X-Token header
AI-assisted analysis of tiangolo/fastapi@3e8d1526d8 (2026-08-11).
Data as JSON: /api/errors/42001f4e533714a0.
Report an issue: GitHub.