{"record":{"id":"2ac4b64bf11d5467","repo":"tiangolo/fastapi","slug":"incorrect-username-or-password-2ac4b6","errorCode":null,"errorMessage":"Incorrect username or password","messagePattern":"Incorrect username or password","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"docs_src/security/tutorial005_py310.py","lineNumber":156,"sourceCode":"            )\n    return user\n\n\nasync def get_current_active_user(\n    current_user: User = Security(get_current_user, scopes=[\"me\"]),\n):\n    if current_user.disabled:\n        raise HTTPException(status_code=400, detail=\"Inactive user\")\n    return current_user\n\n\n@app.post(\"/token\")\nasync def login_for_access_token(\n    form_data: OAuth2PasswordRequestForm = Depends(),\n) -> Token:\n    user = authenticate_user(fake_users_db, form_data.username, form_data.password)\n    if not user:\n        raise HTTPException(status_code=400, detail=\"Incorrect username or password\")\n    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)\n    access_token = create_access_token(\n        data={\"sub\": user.username, \"scope\": \" \".join(form_data.scopes)},\n        expires_delta=access_token_expires,\n    )\n    return Token(access_token=access_token, token_type=\"bearer\")\n\n\n@app.get(\"/users/me/\")\nasync def read_users_me(current_user: User = Depends(get_current_active_user)) -> User:\n    return current_user\n\n\n@app.get(\"/users/me/items/\")\nasync def read_own_items(\n    current_user: User = Security(get_current_active_user, scopes=[\"items\"]),\n):\n    return [{\"item_id\": \"Foo\", \"owner\": current_user.username}]","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/tiangolo/fastapi/blob/3e8d1526d83a90aaf7d6eb6dc682bf150f180b25/docs_src/security/tutorial005_py310.py#L138-L174","documentation":"Legacy-DI variant of error 53. The /token handler raises HTTP 400 'Incorrect username or password' when authenticate_user is falsy (unknown username or failed password verify, the latter timed against DUMMY_HASH for unknown users). Note the 400-vs-401 inconsistency with tutorial004.","triggerScenarios":"POST /token form-encoded with username not in {johndoe, alice} or a wrong password.","commonSituations":"Wrong password; missing username; regenerated hashes; JSON body instead of form data.","solutions":["POST username + password matching a seeded user.","Regenerate stored hashes if the hasher changed.","Use 401 with WWW-Authenticate for RFC 6749 consistency."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"import httpx\ndef post_token(client: httpx.Client, u: str, p: str, scopes: str = \"\"):\n    assert u and p, \"username/password required\"\n    data = {\"username\": u, \"password\": p}\n    if scopes:\n        data[\"scope\"] = scopes\n    return client.post(\"/token\", data=data)","typeGuard":"from typing import TypeGuard\ndef is_valid_creds(pair: tuple) -> TypeGuard[tuple[str, str]]:\n    u, p = pair\n    return isinstance(u, str) and isinstance(p, str) and u.strip() and p","tryCatchPattern":"import httpx\ntry:\n    r = httpx.post(\"/token\", data={\"username\": u, \"password\": p, \"scope\": \"me items\"})\n    r.raise_for_status()\nexcept httpx.HTTPStatusError as e:\n    if e.response.status_code in (400, 401):\n        show_generic_login_error()","preventionTips":["POST form-encoded credentials and scopes.","Regenerate stored hashes when the hasher changes.","Do not auto-retry with the same credentials."],"tags":["fastapi","authentication","oauth2","scopes","python"],"backgroundTag":null,"analyzedSha":"3e8d1526d83a90aaf7d6eb6dc682bf150f180b25","analyzedAt":"2026-08-11T02:34:52.986Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}