infiniflow/ragflow · error · Exception

Same email: {user_info.email} exists!

Error message

Same email: {user_info.email} exists!

What it means

Raised during OAuth registration when the query after user_register() returns more than one row for the same email — the user table contains duplicates, violating the single-account-per-email assumption. Registration is rolled back and the browser is redirected to /?error=Same email ... exists!.

Source

Thrown at api/apps/restful_apis/user_api.py:248

                    avatar = ""

                users = user_register(
                    user_id,
                    {
                        "access_token": get_uuid(),
                        "email": user_info.email,
                        "avatar": avatar,
                        "nickname": user_info.nickname,
                        "login_channel": channel,
                        "last_login_time": get_format_time(),
                        "is_superuser": False,
                    },
                )

                if not users:
                    raise Exception(f"Failed to register {user_info.email}")
                if len(users) > 1:
                    raise Exception(f"Same email: {user_info.email} exists!")

                # Try to log in
                user = users[0]
                login_user(user)
                return redirect(f"/?auth={user.get_id()}")

            except Exception as e:
                rollback_user_registration(user_id)
                logging.exception(e)
                return redirect(f"/?error={str(e)}")

        # User exists, try to log in
        user = users[0]
        user.access_token = get_uuid()
        if user and hasattr(user, "is_active") and user.is_active == "0":
            return redirect("/?error=user_inactive")

        login_user(user)

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Deduplicate the user table: SELECT email, COUNT(*) FROM user GROUP BY email HAVING COUNT(*)>1, then merge/remove the extra rows.
  2. Add/restore a unique constraint on user.email to prevent recurrence.
  3. If caused by concurrent callbacks, retry login after the first transaction commits — the existing-user path will then log in.
  4. Verify rollback_user_registration cleaned the half-inserted row.

Example fix

-- find duplicates
SELECT email, COUNT(*) FROM user GROUP BY email HAVING COUNT(*) > 1;
-- keep one row per email, delete the others, then:
ALTER TABLE user ADD UNIQUE KEY uq_user_email (email);
Defensive patterns

Strategy: validation

Validate before calling

def email_unique(email):
    users = UserService.query(email=email)
    return len(users) <= 1

assert email_unique(user_info.email), f"duplicate rows for {user_info.email} - dedupe user table"

Try / catch

try:
    user = complete_oauth_registration(user_info)
except Exception as e:
    if "Same email" in str(e):
        users = UserService.query(email=user_info.email)
        if users:
            login_user(users[0])  # recover by logging into the existing account
        else:
            raise

Prevention

When it happens

Trigger: OAuth callback for a new email where the users lookup returns len>1: pre-existing duplicate rows in the user table (from historical bugs or manual inserts) for that email.

Common situations: Databases migrated from older versions that allowed duplicates, concurrent OAuth callbacks for the same new email racing the insert, or test fixtures creating users with the same email.

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/6bc1924aeebc6270. Report an issue: GitHub.