{"record":{"id":"dece209b769e308d","repo":"makeplane/plane","slug":"5017","errorCode":"5017","errorMessage":"BOT_USER_LOGIN_FORBIDDEN","messagePattern":"BOT_USER_LOGIN_FORBIDDEN","errorType":"exception","errorClass":"AuthenticationException","httpStatus":null,"severity":"error","filePath":"apps/api/plane/authentication/adapter/base.py","lineNumber":340,"sourceCode":"        # provisioned account that was never deactivated has last_logout_time=None\n        # and is allowed through for its first login; an account deactivated via\n        # the API has last_logout_time set and is blocked regardless of whether\n        # it had previously logged in.\n        if user and not user.is_active and user.last_logout_time is not None:\n            raise AuthenticationException(\n                error_code=AUTHENTICATION_ERROR_CODES[\"USER_ACCOUNT_DEACTIVATED\"],\n                error_message=\"USER_ACCOUNT_DEACTIVATED\",\n                payload={\"email\": email},\n            )\n\n        # Reject bot service accounts (BOT_USER_LOGIN_FORBIDDEN). Bots (is_bot=True,\n        # e.g. the WORKSPACE_SEED bot) are internal identities that act only through\n        # API tokens; they must never be assumable via the interactive login/signup\n        # flow (email/password, magic code, or any OAuth provider). A brand-new\n        # signup can never be a bot — bots are provisioned internally, never through\n        # this path — so guarding on an existing `user` record is sufficient.\n        if user and user.is_bot:\n            raise AuthenticationException(\n                error_code=AUTHENTICATION_ERROR_CODES[\"BOT_USER_LOGIN_FORBIDDEN\"],\n                error_message=\"BOT_USER_LOGIN_FORBIDDEN\",\n                payload={\"email\": email},\n            )\n\n        # True = new user (signup), False = returning user (login)\n        is_signup = not bool(user)\n        # If user is not present, create a new user\n        if not user:\n            # New user\n            self.__check_signup(email)\n\n            # Initialize user\n            user = User(email=email, username=uuid.uuid4().hex)\n\n            # Check if password is autoset\n            if self.user_data.get(\"user\").get(\"is_password_autoset\"):\n                user.set_password(uuid.uuid4().hex)","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/makeplane/plane/blob/1c8a60f858d8472aa56e29994ec1c7926da2c6ce/apps/api/plane/authentication/adapter/base.py#L322-L358","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Do not log in as a bot interactively; generate and use an API token for that bot identity instead.","If a human must own that email, create a separate non-bot User account with a different email and migrate ownership.","Confirm the target account is actually a bot (User.is_bot) before assuming misuse."],"exampleFix":"# before: trying to sign in as bot@workspace via UI -> 5017\n# after: use an API token for the bot\ncurl -H \"X-API-Key: <bot-api-token>\" https://plane.example.com/api/...","handlingStrategy":"validation","validationCode":"from plane.db.models import User\n\ndef is_interactive_login_allowed(email: str) -> bool:\n    u = User.objects.filter(email=email).first()\n    return u is None or not u.is_bot  # None = new signup, allowed","typeGuard":"def is_bot_identity(user) -> bool:\n    return user is not None and bool(user.is_bot)","tryCatchPattern":"try:\n    adapter_login(email, password)\nexcept AuthenticationException as e:\n    if e.error_code == 5017:\n        suggest_api_token_for(email=e.payload.get('email'))\n    else:\n        raise","preventionTips":["Never reuse a bot identity's email for human login.","Provision API tokens for automation instead of trying to log in interactively."],"tags":["authentication","bot-account","security","login","api-token"],"backgroundTag":null,"analyzedSha":"1c8a60f858d8472aa56e29994ec1c7926da2c6ce","analyzedAt":"2026-08-12T14:44:31.636Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}