infiniflow/ragflow · error · AdminException

User {email} inactive

Error message

User {email} inactive

What it means

AdminException(403) from login_admin (admin/server/auth.py:167): credentials are valid and the user is a superuser, but is_active == ActiveEnum.INACTIVE.value, so the account is disabled and cannot open an admin session. Raised before login_user()/token minting, so no session is created.

Source

Thrown at admin/server/auth.py:167

    return wrapper


def login_admin(email: str, password: str):
    """
    :param email: admin email
    :param password: string before decrypt (RSA encrypted + base64 encoded)
    """
    users = UserService.query(email=email)
    if not users:
        raise UserNotFoundError(email)
    decrypted = decrypt(password)
    user = UserService.query_user(email, decrypted)
    if not user:
        raise AdminException("Email and password do not match!")
    if not user.is_superuser:
        raise AdminException("Not admin", 403)
    if user.is_active == ActiveEnum.INACTIVE.value:
        raise AdminException(f"User {email} inactive", 403)

    resp = user.to_json()
    user.access_token = get_uuid()
    login_user(user)
    user.update_time = (current_timestamp(),)
    user.update_date = (datetime_format(datetime.now()),)
    user.last_login_time = get_format_time()
    user.save()
    msg = "Welcome back!"
    return sync_construct_response(data=resp, auth=user.get_id(), message=msg)


def check_admin(username: str, password: str):
    users = UserService.query(email=username)
    if not users:
        logging.info(f"Username: {username} is not registered!")
        user_info = {
            "id": uuid.uuid1().hex,

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Reactivate the account: UPDATE user SET is_active='1' WHERE email='<email>'; then retry login.
  2. If every superuser is inactive, fix it in the DB before restarting the server (otherwise error 62 blocks startup).
  3. Keep at least one active superuser at all times when running deactivation workflows.
Defensive patterns

Strategy: try-catch

Validate before calling

rows = UserService.query(email=email) or []
from common.constants import ActiveEnum
if rows and rows[0].is_active == ActiveEnum.INACTIVE.value:
    raise PermissionError(f"{email} is deactivated; login_admin will return 403 inactive")

Type guard

def login_will_pass(user) -> bool:
    return bool(user) and bool(user.is_superuser) and user.is_active != ActiveEnum.INACTIVE.value

Try / catch

from api.common.exceptions import AdminException
try:
    login_admin(email, password)
except AdminException as e:
    if "inactive" in str(e):
        # reactivate the account in the DB, then retry
        raise

Prevention

When it happens

Trigger: POST /admin/login with a deactivated superuser's email/password (e.g. disabled via admin UI or direct DB edit). Everything up to and including password verification succeeds, then the active check fails.

Common situations: Disabled built-in admin account after a security review; is_active flipped by a bulk user-management script; restoring backups where the flag wasn't carried over.

Related errors


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