apereo/cas · error · AccountNotFoundException
AccountNotFoundException
Error message
AccountNotFoundException
What it means
RedisAuthenticationHandler.authenticateUsernamePasswordInternal throws AccountNotFoundException (no-arg, so the message is just the class name) when redisTemplate has no value stored under credential.getUsername() — i.e. the user account does not exist in Redis.
Solutions
- Provision/load the user account into Redis under the exact username key
- Verify CAS and the provisioning tool point at the same Redis host, port, database and key prefix
- Check for TTL/eviction policies (maxmemory-policy) deleting account keys
- Confirm the username casing matches exactly what is stored in Redis
Example fix
// provisioning (before login)
redisTemplate.opsForValue().set("jsmith", new RedisUserAccount("jsmith", encoder.encode("secret"), Status.OK, attrs));
// then CAS login with credential username 'jsmith' succeeds instead of throwing AccountNotFoundException Defensive patterns
Strategy: validation
Validate before calling
Object raw = redisTemplate.opsForValue().get(username);
if (raw == null) {
// account not provisioned; skip login or trigger provisioning
} Type guard
boolean accountExists(RedisTemplate<String,Object> tpl, String username) {
return Boolean.TRUE.equals(tpl.hasKey(username));
} Try / catch
try {
authHandler.authenticate(credential);
} catch (AccountNotFoundException e) {
// 'user not registered' messaging
} Prevention
- Provision accounts before enabling Redis authentication
- Pin the same Redis database/index for writer and CAS
- Avoid volatile eviction policies for account keys
- Normalize username casing
When it happens
Trigger: A login attempt where GET <username> on the configured Redis template returns null: key never written, wrong Redis database/index configured, key expired or evicted, or key naming/prefix mismatch.
Common situations: User provisioning never loaded accounts into Redis; Redis flushed or running against the wrong logical DB (spring.data.redis.database); TTL expiry removed the account; username case sensitivity mismatch between CAS and the provisioning writer.
Understand the failure class
Background: "User not found", "Invalid user", and "does not exist": what missing-user lookup errors mean across Rocket.Chat, LiteLLM, Phabricator, rustfs, and pnpm — this error's family across 10 libraries.
Related errors
- FailedLoginException
- AccountDisabledException
- AccountExpiredException
- Radius authentication failed for user
- Radius authentication failed for user
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/16283c3b6ba47a52.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-redis-authentication/src/main/java/org/apereo/cas/redis/RedisAuthenticationHandler.java:37
*/
@Slf4j
public class RedisAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {
private final CasRedisTemplate redisTemplate;
public RedisAuthenticationHandler(final String name,
final PrincipalFactory principalFactory, final Integer order,
final CasRedisTemplate redisTemplate) {
super(name, principalFactory, order);
this.redisTemplate = redisTemplate;
}
@Override
protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(
final UsernamePasswordCredential credential,
final String originalPassword) throws Throwable {
val account = (RedisUserAccount) redisTemplate.opsForValue().get(credential.getUsername());
if (account == null) {
throw new AccountNotFoundException();
}
if (!getPasswordEncoder().matches(originalPassword, account.getPassword())) {
LOGGER.warn("Account password on record for [{}] does not match the given/encoded password", credential.getId());
throw new FailedLoginException();
}
switch (account.getStatus()) {
case DISABLED -> throw new AccountDisabledException();
case EXPIRED -> throw new AccountExpiredException();
case LOCKED -> throw new AccountLockedException();
case MUST_CHANGE_PASSWORD -> throw new AccountPasswordMustChangeException();
case OK -> LOGGER.debug("Account status is OK");
}
val principal = principalFactory.createPrincipal(account.getUsername(), account.getAttributes());
return createHandlerResult(credential, principal, new ArrayList<>());
}
}
View on GitHub (pinned to e7288fc434)