apereo/cas · error · FailedLoginException

Cannot get connection from pool to validate SPNEGO Token

Error message

Cannot get connection from pool to validate SPNEGO Token

What it means

The handler borrows a JCIFS 'authentications' object from an internal connection pool with cas.authn.spnego.pool-timeout as the wait limit. If poll() returns null (pool exhausted / no connection available within the timeout), authentication fails with this FailedLoginException.

Solutions

  1. Increase cas.authn.spnego.pool-timeout (e.g. to a few seconds) so the handler waits longer for a free connection.
  2. Increase the JCIFS pool size (jcifs.smb.pool related properties / pool configuration in SpnegoConfiguration) to match peak concurrency.
  3. Investigate why connections are held long: check KDC/Domain Controller latency.
  4. Add monitoring/retries around SPNEGO login spikes; scale CAS instances horizontally.

Example fix

// before
cas.authn.spnego.pool-timeout=PT0.5S
// after
cas.authn.spnego.pool-timeout=PT10S
Defensive patterns

Strategy: retry

Validate before calling

// size check: pool size >= expected concurrent SPNEGO logins
poolSize >= peakLoginsPerSecond * avgAcquireMillis / 1000

Try / catch

try {
    return handler.authenticate(credential);
} catch (FailedLoginException e) {
    if (e.getMessage().contains("connection from pool")) {
        // brief backoff then retry once
        Thread.sleep(100);
        return handler.authenticate(credential);
    }
    throw e;
}

Prevention

When it happens

Trigger: Concurrent SPNEGO authentications exceed the pool capacity (jcifs maxPoolSize/pool settings), or every pooled connection is busy, so pool.poll(poolTimeout) times out and returns null.

Common situations: Login bursts during mass events (start-of-day); pool size configured too small; slow KDC causing pooled connections to be held long; pool-timeout set to a few milliseconds.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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

Appendix: source

Thrown at support/cas-server-support-spnego/src/main/java/org/apereo/cas/support/spnego/authentication/handler/support/JcifsSpnegoAuthenticationHandler.java:66

    protected AuthenticationHandlerExecutionResult doAuthentication(final Credential credential, final Service service) throws Throwable {
        val spnegoCredential = (SpnegoCredential) credential;
        if (!spnegoProperties.isNtlmAllowed() && spnegoCredential.isNtlm()) {
            throw new FailedLoginException("NTLM not allowed");
        }

        try {
            LOGGER.debug("Waiting for connection to validate SPNEGO Token");
            val poolTimeoutInMilliseconds = Beans.newDuration(spnegoProperties.getPoolTimeout()).toMillis();
            val authentications = authenticationsPool.poll(poolTimeoutInMilliseconds, TimeUnit.MILLISECONDS);
            if (authentications != null) {
                try {
                    return doInternalAuthentication(authentications, spnegoCredential, service);
                } finally {
                    authenticationsPool.add(authentications);
                    LOGGER.debug("Returned connection to pool");
                }
            }
            throw new FailedLoginException("Cannot get connection from pool to validate SPNEGO Token");
        } catch (final InterruptedException e) {
            throw new FailedLoginException("Thread interrupted while waiting for connection to validate SPNEGO Token");
        }
    }

    protected AuthenticationHandlerExecutionResult doInternalAuthentication(final List<Authentication> authentications,
                                                                            final SpnegoCredential spnegoCredential, final Service service) throws Throwable {
        var principal = (java.security.Principal) null;
        var nextToken = (byte[]) null;
        val it = authentications.iterator();
        while (nextToken == null && it.hasNext()) {
            try {
                val authentication = it.next();
                authentication.reset();
                LOGGER.debug("Processing SPNEGO authentication");
                authentication.process(spnegoCredential.getInitToken());
                principal = authentication.getPrincipal();
                LOGGER.debug("Authenticated SPNEGO principal [{}]. Retrieving the next token for authentication...",

View on GitHub (pinned to e7288fc434)