apereo/cas · warning

Throttling submission from

Error message

Throttling submission from [{}]. Authentication attempt exceeds the failure threshold [{}]

What it means

AbstractThrottledSubmissionHandlerInterceptorAdapter.preHandle decides before authentication whether the request should be throttled. When throttleRequest or exceedsThreshold indicates the failure threshold is met, this warning logs the client's remote address and the configured threshold rate; the throttle is recorded, the submission updated, and the configured throttled request response handler rejects the request.

Solutions

  1. Investigate the source address for attack activity and block it upstream if malicious.
  2. Raise the threshold or shorten the range so legitimate users are not throttled.
  3. Configure a custom ThrottledRequestResponseHandler to return an appropriate response (e.g. 429).
  4. For shared NAT scenarios, switch the throttle key to username-based instead of IP-based.

Example fix

// before: key by IP only (NAT problem)
cas.authn.throttle.app-code=CAS
cas.authn.throttle.failure.code=AuthenticationFailed
// consider username-based throttle handler or raise threshold
cas.authn.throttle.failure.threshold=20
Defensive patterns

Strategy: retry

Validate before calling

// client: avoid rapid consecutive failed attempts
if (lastAuthFailure != null && Duration.between(lastAuthFailure, Instant.now()).getSeconds() < 5)
    throw new IllegalStateException("Wait before retrying authentication");

Try / catch

// back off when throttled
try {
    casLogin(user, pass);
} catch (HttpClientErrorException e) {
    if (e.getStatusCode().value() == 429) {
        Thread.sleep(backoffSeconds++ * 1000L);
        casLogin(user, pass);
    } else throw e;
}

Prevention

When it happens

Trigger: preHandle finds the request's key already exceeds the failure threshold (via throttleRequest(request,response) or exceedsThreshold(request)) — i.e. too many failed authentications from the same username/IP within the configured rate window.

Common situations: Brute-force/credential-stuffing attempts against /login; misbehaving integration tests replaying failed logins; shared NAT IP causing many users to trip the per-IP threshold.

Understand the failure class

Related errors


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

Appendix: source

Thrown at support/cas-server-support-throttle-core/src/main/java/org/apereo/cas/throttle/AbstractThrottledSubmissionHandlerInterceptorAdapter.java:67

    @Override
    public void afterPropertiesSet() {
        val throttle = getConfigurationContext().getCasProperties().getAuthn().getThrottle().getFailure();
        this.thresholdRate = (double) throttle.getThreshold() / throttle.getRangeSeconds();
        LOGGER.trace("Calculated threshold rate as [{}]", this.thresholdRate);
    }

    @Override
    public final boolean preHandle(final @NonNull HttpServletRequest request,
                                   final @NonNull HttpServletResponse response,
                                   final @NonNull Object handler) {
        if (isRequestIgnoredForThrottling(request, response)) {
            LOGGER.trace("Letting the request through without throttling; No request filters support it");
            return true;
        }

        val throttled = throttleRequest(request, response) || exceedsThreshold(request);
        if (throttled) {
            LOGGER.warn("Throttling submission from [{}]. Authentication attempt exceeds the failure threshold [{}]",
                request.getRemoteAddr(), this.thresholdRate);
            recordThrottle(request);
            updateThrottledSubmission(request);
            return configurationContext.getThrottledRequestResponseHandler().handle(request, response);
        }
        return true;
    }

    @Override
    public final void postHandle(final @NonNull HttpServletRequest request, final @NonNull HttpServletResponse response,
                                 final @NonNull Object handler, final ModelAndView modelAndView) {
        if (isRequestIgnoredForThrottling(request, response)) {
            LOGGER.trace("Skipping authentication throttling for requests; no filters support it.");
            return;
        }

        val recordEvent = shouldResponseBeRecordedAsFailure(response);
        if (recordEvent) {

View on GitHub (pinned to e7288fc434)