makeplane/plane · error · AuthenticationException

5017

5017

Error message

BOT_USER_LOGIN_FORBIDDEN

What it means

Thrown at base.py:340 when the resolved user has is_bot=True. Bot identities (e.g. the WORKSPACE_SEED bot) are internal service accounts that act only through API tokens and must never be assumable via email/password, magic code, or OAuth. Because bots are provisioned internally and never created through the signup path, guarding only on an existing user record is sufficient.

Source

Thrown at apps/api/plane/authentication/adapter/base.py:340

        # provisioned account that was never deactivated has last_logout_time=None
        # and is allowed through for its first login; an account deactivated via
        # the API has last_logout_time set and is blocked regardless of whether
        # it had previously logged in.
        if user and not user.is_active and user.last_logout_time is not None:
            raise AuthenticationException(
                error_code=AUTHENTICATION_ERROR_CODES["USER_ACCOUNT_DEACTIVATED"],
                error_message="USER_ACCOUNT_DEACTIVATED",
                payload={"email": email},
            )

        # Reject bot service accounts (BOT_USER_LOGIN_FORBIDDEN). Bots (is_bot=True,
        # e.g. the WORKSPACE_SEED bot) are internal identities that act only through
        # API tokens; they must never be assumable via the interactive login/signup
        # flow (email/password, magic code, or any OAuth provider). A brand-new
        # signup can never be a bot — bots are provisioned internally, never through
        # this path — so guarding on an existing `user` record is sufficient.
        if user and user.is_bot:
            raise AuthenticationException(
                error_code=AUTHENTICATION_ERROR_CODES["BOT_USER_LOGIN_FORBIDDEN"],
                error_message="BOT_USER_LOGIN_FORBIDDEN",
                payload={"email": email},
            )

        # True = new user (signup), False = returning user (login)
        is_signup = not bool(user)
        # If user is not present, create a new user
        if not user:
            # New user
            self.__check_signup(email)

            # Initialize user
            user = User(email=email, username=uuid.uuid4().hex)

            # Check if password is autoset
            if self.user_data.get("user").get("is_password_autoset"):
                user.set_password(uuid.uuid4().hex)

View on GitHub (pinned to 1c8a60f858)

Solutions

  1. Do not log in as a bot interactively; generate and use an API token for that bot identity instead.
  2. If a human must own that email, create a separate non-bot User account with a different email and migrate ownership.
  3. Confirm the target account is actually a bot (User.is_bot) before assuming misuse.

Example fix

# before: trying to sign in as bot@workspace via UI -> 5017
# after: use an API token for the bot
curl -H "X-API-Key: <bot-api-token>" https://plane.example.com/api/...
Defensive patterns

Strategy: validation

Validate before calling

from plane.db.models import User

def is_interactive_login_allowed(email: str) -> bool:
    u = User.objects.filter(email=email).first()
    return u is None or not u.is_bot  # None = new signup, allowed

Type guard

def is_bot_identity(user) -> bool:
    return user is not None and bool(user.is_bot)

Try / catch

try:
    adapter_login(email, password)
except AuthenticationException as e:
    if e.error_code == 5017:
        suggest_api_token_for(email=e.payload.get('email'))
    else:
        raise

Prevention

When it happens

Trigger: Any interactive login/signup where the email resolves to an existing User with is_bot=True raises AuthenticationException code 5017 with payload {email}. Hit when someone tries to sign in with the email address that a bot service account was registered under.

Common situations: An admin or script attempts to log in as the WORKSPACE_SEED bot or another bot user via the web UI or /auth/ endpoints. Also occurs if a bot's email was reused for a human account elsewhere and the human tries the wrong identity.

Understand the failure class

Related errors


AI-assisted analysis of makeplane/plane@1c8a60f858 (2026-08-12). Data as JSON: /api/errors/dece209b769e308d. Report an issue: GitHub.