{"record":{"id":"7fd611a176bcf403","repo":"bytedance/deer-flow","slug":"invalid-credentials","errorCode":"invalid_credentials","errorMessage":"Incorrect email or password","messagePattern":"Incorrect email or password","errorType":"http","errorClass":"HTTPException","httpStatus":401,"severity":"error","filePath":"backend/app/gateway/routers/auth.py","lineNumber":303,"sourceCode":"# ── Endpoints ─────────────────────────────────────────────────────────────\n\n\n@router.post(\"/login/local\", response_model=LoginResponse)\nasync def login_local(\n    request: Request,\n    response: Response,\n    form_data: OAuth2PasswordRequestForm = Depends(),\n    remember_me: bool = Form(default=True),\n):\n    \"\"\"Local email/password login.\"\"\"\n    client_ip = _get_client_ip(request)\n    _check_rate_limit(client_ip)\n\n    user = await get_local_provider().authenticate({\"email\": form_data.username, \"password\": form_data.password})\n\n    if user is None:\n        _record_login_failure(client_ip)\n        raise HTTPException(\n            status_code=status.HTTP_401_UNAUTHORIZED,\n            detail=AuthErrorResponse(code=AuthErrorCode.INVALID_CREDENTIALS, message=\"Incorrect email or password\").model_dump(),\n        )\n\n    _record_login_success(client_ip)\n    token = create_access_token(str(user.id), token_version=user.token_version)\n    _set_session_cookie(response, token, request, remember_me=remember_me)\n\n    return LoginResponse(\n        expires_in=get_auth_config().token_expiry_days * 24 * 3600,\n        needs_setup=user.needs_setup,\n    )\n\n\ndef _local_registration_enabled() -> bool:\n    \"\"\"Whether visitors may self-register a local account.\n\n    Local registration bypasses the OIDC provisioning policy entirely","sourceCodeStart":285,"sourceCodeEnd":321,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/routers/auth.py#L285-L321","documentation":"401 from POST /api/auth/login: the local provider's authenticate() returned None — no user with that email exists or the password hash does not verify. The response body is a structured AuthErrorResponse with code 'invalid_credentials', and the failure is recorded against the client IP for the rate limiter.","triggerScenarios":"POST /api/auth/login (OAuth2 form: username=email, password) with an unregistered email, a wrong password, or a user record whose password_hash does not match. Also when the user was created via OAuth and has no local password.","commonSituations":"Password reset not yet applied; user registered through SSO but attempts email/password login; stale token_version or migrated user store with un-migrated hashes; typos in email.","solutions":["Verify the email is registered locally (or use the password-reset flow) and retype the password","If the account is OAuth-only, log in via the SSO provider instead of local credentials","Administrators can reset the user's password in the user store to restore local login","Distinguish from lockout: 401 means credentials wrong; 429 means locked out"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await login(email, password); } catch (e) {\n  if (e.status === 401 && e.body?.code === 'invalid_credentials') { showCredentialError(); return; }\n  throw e;\n}","preventionTips":["Surface 'invalid credentials' distinctly from 429 lockout and 422 validation errors","Offer password reset on repeated 401s instead of blind retries"],"tags":["auth","http-401","login","invalid-credentials"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}