theonedev/onedev · error · DisabledAccountException

Service or AI account not allowed to login

Error message

Service or AI account not allowed to login

What it means

During authentication, after resolving the user, OneDev checks the user's type. Only ORDINARY (human) users may log in via the username/password path; SERVICE and AI accounts are rejected with a DisabledAccountException. Service accounts are intended for internal use (e.g. system operations) and AI accounts for agent access, so interactive password login is blocked for them.

Source

Thrown at server-core/src/main/java/io/onedev/server/security/DefaultAuthenticatingService.java:145

			emailAddressService.create(emailAddress);
		}
		syncGroupsAndSshKeys(user, false, authenticated, defaultGroupName);
	}
	
	@Override
	protected final AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) 
			throws AuthenticationException {
		return transactionService.call(() -> {
			try {
				var userName = normalizeUserName((String) token.getPrincipal());
				var user = userService.findByVerifiedEmailAddress((String) token.getPrincipal());
				if (user == null) 
					user = userService.findByName(userName);
				if (user != null) {
					if (user.isDisabled())
						throw new DisabledAccountException(_T("Account is disabled"));
					else if (user.getType() != ORDINARY)
						throw new DisabledAccountException(_T("Service or AI account not allowed to login"));
					if (user.getPassword() == null) {
						var authenticator = settingService.getAuthenticator();
						if (authenticator != null) {
							var authenticated = authenticator.authenticate((UsernamePasswordToken) token);
							var emailAddressValue = authenticated.getEmail();
							if (emailAddressValue != null) {
								var emailAddress = emailAddressService.findByValue(emailAddressValue);
								if (emailAddress != null) {
									if (emailAddress.getOwner().equals(user) || !emailAddress.isVerified()) {
										updateUser(user, authenticated, emailAddress, authenticator.getDefaultGroup());
										return user;
									} else {
										throw new AuthenticationException(MessageFormat.format(_T("Email address \"{0}\" already used by another account"), emailAddressValue));
									}
								} else {
									updateUser(user, authenticated, null, authenticator.getDefaultGroup());
									return user;
								}

View on GitHub (pinned to d44925c47c)

Solutions

  1. Use an access token issued for the account instead of username/password where the API/git allows it.
  2. If interactive login is genuinely needed, change the account type back to Ordinary in Administration -> Users.
  3. Create a dedicated ordinary user for human-style access and grant it the needed permissions.
  4. For AI/service integrations, use the integration mechanisms intended for those account types rather than password auth.

Example fix

// before: service account password login
curl -u svc-bot:password https://onedev.example.com/api/projects
// after: use an access token for the account
curl -H "Authorization: Bearer <access-token>" https://onedev.example.com/api/projects
Defensive patterns

Strategy: validation

Validate before calling

if (user.getType() !== 'ORDINARY') {
  throw new Error('Use access tokens or intended integration flows for service/AI accounts');
}

Type guard

function isOrdinaryUser(u) { return u != null && u.type === 'ORDINARY'; }

Try / catch

try {
  authenticate(name, password);
} catch (DisabledAccountException e) {
  if (e.getMessage().contains("Service or AI account")) {
    switchToTokenAuth();
  }
}

Prevention

When it happens

Trigger: A login attempt with the credentials (name/password) of a user whose getType() is SERVICE or AI instead of ORDINARY - typically via the sign-in form or HTTP basic auth.

Common situations: Teams create a 'bot' or 'ci' user and set its type to Service to mark it non-human, then try to sign in to the web UI with it; or a script uses the service account's password directly.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/3a211217499ea416. Report an issue: GitHub.