apereo/cas · warning

No risk calculators are available to evaluate…

Error message

No risk calculators are available to evaluate authentication risk. CAS will proceed to regard the authentication attempt as highly risky. Examine your configuration and ensure at least one risk calculator is available and enabled to correctly assess authentication risk.

What it means

DefaultAuthenticationRiskEvaluator.evaluate() requires at least one configured AuthenticationRiskCalculator to produce a risk score. When the calculators collection is empty it logs this warning and returns AuthenticationRiskScore.highestRiskScore(), meaning every authentication is treated as maximally risky — downstream risk response (usually MFA or blocking) will fire for all logins. This is a misconfiguration signal, not a runtime failure of the evaluator itself.

Solutions

  1. Enable at least one risk calculator feature in cas.authn.adaptive.risk (e.g. request, dateTime, or geo calculators) and confirm its module is on the classpath.
  2. Inspect the context for AuthenticationRiskCalculator beans; add a custom one if none exist.
  3. If risk evaluation is not actually needed, disable cas.authn.adaptive.risk entirely so this evaluator is not engaged.
  4. Review startup logs for the auto-configuration of the risk module to see why no calculators were created.

Example fix

// before: risk enabled, no calculators
cas.authn.adaptive.risk.enabled=true
// after: enable at least one calculator source
cas.authn.adaptive.risk.enabled=true
cas.authn.adaptive.risk.request.enabled=true
cas.authn.adaptive.risk.dateTime.enabled=true
Defensive patterns

Strategy: fallback

Validate before calling

val calculators = applicationContext.getBeansOfType(AuthenticationRiskCalculator.class);
if (calculators.isEmpty() && casProperties.getAuthn().getAdaptive().getRisk().isEnabled()) {
    throw new IllegalStateException("Risk enabled but no AuthenticationRiskCalculator beans registered");
}

Try / catch

try {
    val score = riskEvaluator.evaluate(authentication, service, clientInfo);
} catch (Exception e) {
    // fall back to a conservative-but-configured score
    val score = AuthenticationRiskScore.defaultRiskScore();
}

Prevention

When it happens

Trigger: The adaptive/risk feature is enabled (cas.authn.adaptive.risk.enabled=true) but no risk calculator bean (e.g. DefaultAuthenticationRequestRiskCalculator, DefaultAuthenticationDateTimeRiskCalculator, geo-location calculators) is registered or enabled in the application context.

Common situations: Enabling risk-based auth without enabling any of its component features (e.g. not enabling the IP-intelligence/geo modules that contribute calculators); a custom configuration that overrides the evaluator bean and forgets to inject calculators; module dependency missing so calculator auto-configuration never runs.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/dd00618338669365. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-electrofence/src/main/java/org/apereo/cas/impl/engine/DefaultAuthenticationRiskEvaluator.java:48

@Getter
@RequiredArgsConstructor
@Slf4j
@Transactional(readOnly = true, value = CasEventRepository.TRANSACTION_MANAGER_EVENTS)
public class DefaultAuthenticationRiskEvaluator implements AuthenticationRiskEvaluator {
    private final List<AuthenticationRequestRiskCalculator> calculators;
    private final CasConfigurationProperties casProperties;
    private final CasEventRepository casEventRepository;

    @Audit(action = AuditableActions.EVALUATE_RISKY_AUTHENTICATION,
        actionResolverName = AuditActionResolvers.ADAPTIVE_RISKY_AUTHENTICATION_ACTION_RESOLVER,
        resourceResolverName = AuditResourceResolvers.ADAPTIVE_RISKY_AUTHENTICATION_RESOURCE_RESOLVER)
    @Override
    public AuthenticationRiskScore evaluate(final Authentication authentication,
                                            final RegisteredService service,
                                            final ClientInfo clientInfo) {

        if (calculators.isEmpty()) {
            LOGGER.warn("No risk calculators are available to evaluate authentication risk. "
                + "CAS will proceed to regard the authentication attempt as highly risky. Examine your configuration "
                + "and ensure at least one risk calculator is available and enabled to correctly assess authentication risk.");
            return AuthenticationRiskScore.highestRiskScore();
        }

        val scores = calculators
            .stream()
            .map(riskCalculator -> riskCalculator.calculate(authentication, service, clientInfo))
            .filter(Objects::nonNull)
            .toList();

        LOGGER.debug("Collected [{}] risk scores from [{}] risk calculators", scores.size(), calculators.size());
        val sum = scores
            .stream()
            .map(AuthenticationRiskScore::getScore)
            .filter(Objects::nonNull)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        val score = sum.divide(BigDecimal.valueOf(calculators.size()), 2, RoundingMode.UP);

View on GitHub (pinned to e7288fc434)