spring-projects/spring-security · error · DisabledException
User is disabled
Error message
User is disabled
What it means
ActiveDirectoryLdapAuthenticationProvider maps AD bind-failure sub-codes to Spring exceptions. When Active Directory reports the account is disabled (bind error 49 with data 533, ACCOUNT_DISABLED), raiseExceptionForErrorCode throws DisabledException('User is disabled') with the ActiveDirectoryAuthenticationException as cause.
Source
Thrown at ldap/src/main/java/org/springframework/security/ldap/authentication/ad/ActiveDirectoryLdapAuthenticationProvider.java:267
private int parseSubErrorCode(@Nullable String message) {
if (message == null) {
return -1;
}
Matcher matcher = SUB_ERROR_CODE.matcher(message);
if (matcher.matches()) {
return Integer.parseInt(matcher.group(1), 16);
}
return -1;
}
private void raiseExceptionForErrorCode(int code, NamingException exception) {
String hexString = Integer.toHexString(code);
Throwable cause = new ActiveDirectoryAuthenticationException(hexString, exception.getMessage(), exception);
switch (code) {
case PASSWORD_EXPIRED -> throw new CredentialsExpiredException(this.messages
.getMessage("LdapAuthenticationProvider.credentialsExpired", "User credentials have expired"), cause);
case ACCOUNT_DISABLED -> throw new DisabledException(
this.messages.getMessage("LdapAuthenticationProvider.disabled", "User is disabled"), cause);
case ACCOUNT_EXPIRED -> throw new AccountExpiredException(
this.messages.getMessage("LdapAuthenticationProvider.expired", "User account has expired"), cause);
case ACCOUNT_LOCKED -> throw new LockedException(
this.messages.getMessage("LdapAuthenticationProvider.locked", "User account is locked"), cause);
default -> throw badCredentials(cause);
}
}
private String subCodeToLogMessage(int code) {
return switch (code) {
case USERNAME_NOT_FOUND -> "User was not found in directory";
case INVALID_PASSWORD -> "Supplied password was invalid";
case NOT_PERMITTED -> "User not permitted to logon at this time";
case PASSWORD_EXPIRED -> "Password has expired";
case ACCOUNT_DISABLED -> "Account is disabled";
case ACCOUNT_EXPIRED -> "Account expired";
case PASSWORD_NEEDS_RESET -> "User must reset password";View on GitHub (pinned to 96852e8860)
Solutions
- Have an AD administrator enable the account (Set-ADUser -Enabled $true) if access is legitimate.
- Confirm you are authenticating with the intended, active account rather than a deprecated one.
- Catch DisabledException separately to show an account-specific message instead of a generic login failure.
- Verify user search base doesn't resolve to a different, disabled account with the same name.
Example fix
// before
catch (BadCredentialsException e) { return "login?error"; }
// after
catch (DisabledException e) { model.addAttribute("msg", "Account disabled; contact IT"); return "login"; } Defensive patterns
Strategy: try-catch
Try / catch
try {
return authenticationManager.authenticate(token);
} catch (DisabledException e) {
model.addAttribute("msg", "Account disabled; contact IT");
return "login";
} Prevention
- Catch DisabledException separately from BadCredentialsException for distinct UX.
- Keep HR-to-IT account deprovisioning in sync to avoid surprise lockouts of legit users.
- Log the sub-error hex code from ActiveDirectoryAuthenticationException for diagnostics.
- Verify user search base doesn't resolve to a different (disabled) account.
When it happens
Trigger: authenticate() -> handleBindException() -> raiseExceptionForErrorCode(ACCOUNT_DISABLED), raised when an LDAP bind against AD fails with sub-error code 533 indicating the user account is disabled in Active Directory.
Common situations: User left the company and the account was disabled; account administratively disabled pending onboarding/HR action; authenticating with a shared or legacy account that IT disabled.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- User credentials have expired
- User account has expired
- User account is locked
- managerPassword is required if managerDn is supplied
- No BaseLdapPathContextSource instances found. Have you added
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/c69eff642024f656.
Report an issue: GitHub.