{"record":{"id":"7174e64ae51d5b5a","repo":"langgenius/dify","slug":"password-mismatch-7174e6","errorCode":"password_mismatch","errorMessage":"The passwords do not match.","messagePattern":"The passwords do not match\\.","errorType":"error_code","errorClass":"PasswordMismatchError","httpStatus":400,"severity":"error","filePath":"api/controllers/console/auth/forgot_password.py","lineNumber":164,"sourceCode":"@console_ns.route(\"/forgot-password/resets\")\nclass ForgotPasswordResetApi(Resource):\n    @console_ns.doc(\"reset_password\")\n    @console_ns.doc(description=\"Reset password with verification token\")\n    @console_ns.expect(console_ns.models[ForgotPasswordResetPayload.__name__])\n    @console_ns.response(\n        200,\n        \"Password reset successfully\",\n        console_ns.models[ForgotPasswordResetResponse.__name__],\n    )\n    @console_ns.response(400, \"Invalid token or password mismatch\")\n    @setup_required\n    @email_password_login_enabled\n    @model_validate(ForgotPasswordResetPayload)\n    def post(self, req_data: ForgotPasswordResetPayload):\n\n        # Validate passwords match\n        if req_data.new_password != req_data.password_confirm:\n            raise PasswordMismatchError()\n\n        # Validate token and get reset data\n        reset_data = AccountService.get_reset_password_data(req_data.token)\n        if not reset_data:\n            raise InvalidTokenError()\n        # Must use token in reset phase\n        if reset_data.get(\"phase\", \"\") != \"reset\":\n            raise InvalidTokenError()\n\n        # Revoke token to prevent reuse\n        AccountService.revoke_reset_password_token(req_data.token)\n\n        # Generate secure salt and hash password\n        salt = secrets.token_bytes(16)\n        password_hashed = hash_password(req_data.new_password, salt)\n\n        email = reset_data.get(\"email\", \"\")\n        account = AccountService.get_account_by_email_with_case_fallback(email, session=db.session())","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/auth/forgot_password.py#L146-L182","documentation":"Raised by PasswordMismatchError in ForgotPasswordResetApi.post before any token lookup. The payload's new_password and password_confirm are compared directly; mismatch aborts the reset. Mirrors error 405 but for the password-reset completion step.","triggerScenarios":"POST /console/api/forgot-password/reset with new_password != password_confirm. Pure validation guard that runs first, before the token is examined.","commonSituations":"Password manager fills one field only; caps-lock; different keyboard layout; user types different values in the two boxes; frontend omits its own equality check.","solutions":["Compare the two fields in the UI and disable submit until they match.","Show a live mismatch indicator under the confirm field.","Clear both fields and re-enter rather than patching.","Send both fields from the same source value, not two independent inputs."],"exampleFix":"// before\n<button disabled={!newPassword}>Reset</button>\n// after\ndisabled={newPassword !== passwordConfirm || !newPassword}","handlingStrategy":"validation","validationCode":"if (newPassword !== passwordConfirm) {\n  setFieldError('password_confirm', 'Passwords do not match');\n  return;\n}","typeGuard":"function passwordsMatch(a, b) { return typeof a === 'string' && a === b && a.length > 0; }","tryCatchPattern":null,"preventionTips":["Disable submit until both fields are non-empty and equal.","Show a real-time match indicator.","Send both fields from the same state value."],"tags":["auth","forgot-password","validation","password"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}