apereo/cas · warning

warn(logger, getMessage(throwable), throwable)

Error message

warn(logger, getMessage(throwable), throwable)

What it means

In AbstractTicketRegistry.getTicket, when the retrieved ticket is expired, the registry attempts to delete it and (for TGTs) publish a CasTicketGrantingTicketDestroyedEvent. Any exception during that cleanup is swallowed and logged at warn level via LoggingUtils.warn(logger, throwable). Operationally the getTicket still returns no ticket; this warning indicates the expired-ticket cleanup path failed.

Solutions

  1. Inspect the attached throwable (logged via LoggingUtils.warn) to find the root cause — usually ticket-registry backend connectivity or serialization.
  2. Verify the ticket registry store (Redis/Mongo/etc.) is reachable and credentials/network are healthy.
  3. Check registered CasTicketGrantingTicketDestroyedEvent listeners for exceptions they throw; fix or make them defensive.
  4. If stale/deserialization-prone entries accumulate, clean the registry of old tickets; the warning is non-fatal to the caller (ticket is treated as absent).

Example fix

// before
try { deleteTicket(ticket); ... } catch (final Exception e) { LoggingUtils.warn(LOGGER, e); } // hides backend outage
// after
// monitor LOGGER.warn from AbstractTicketRegistry and alert on backend exceptions:
// add health checks/retries for the ticket registry datasource (e.g. Redis ping) instead of relying on swallowed cleanup errors
Defensive patterns

Strategy: try-catch

Try / catch

// caller: expired ticket simply returns null; guard the null
Ticket t = ticketRegistry.getTicket(id);
if (t == null) { /* treat as unauthenticated; also alert on AbstractTicketRegistry warn logs */ }

Prevention

When it happens

Trigger: Calling ticketRegistry.getTicket(id) for an expired ticket where deleteTicket(ticket) or the destroyed-event publication throws — e.g. underlying storage (Redis/Mongo/JDBC/Hazelcast) unreachable, serialization failure, or listener error during event publish.

Common situations: Transient backend outages during high-traffic validation; deserialization errors of stale tickets in the registry; event listeners throwing inside CasTicketGrantingTicketDestroyedEvent handlers.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/registry/AbstractTicketRegistry.java:124

    @Override
    public @Nullable Ticket getTicket(final String ticketId) {
        val returnTicket = getTicket(ticketId, ticket -> {
            if (ticket.isExpired()) {
                val ticketAgeSeconds = getTicketAgeSeconds(ticket);
                LOGGER.debug("Ticket [{}] has expired according to policy [{}] after [{}] seconds and [{}] uses and will be removed from the ticket registry",
                    ticketId, ticket.getExpirationPolicy().getName(), ticketAgeSeconds, ticket.getCountOfUses());
                val clientInfo = ClientInfoHolder.getClientInfo();
                if (ticket instanceof final TicketGrantingTicket tgt) {
                    applicationContext.publishEvent(new CasRequestSingleLogoutEvent(this, tgt, clientInfo));
                }
                try {
                    deleteTicket(ticket);
                    if (ticket instanceof final TicketGrantingTicket tgt) {
                        applicationContext.publishEvent(new CasTicketGrantingTicketDestroyedEvent(this, tgt, clientInfo));
                    }
                } catch (final Exception e) {
                    LoggingUtils.warn(LOGGER, e);
                }
                return false;
            }
            return true;
        });
        if (returnTicket != null) {
            val ticketAgeSeconds = getTicketAgeSeconds(returnTicket);
            if (ticketAgeSeconds < -1) {
                LOGGER.warn("Ticket created [{}] second(s) in the future. Check time synchronization on all servers.", ticketAgeSeconds * -1);
            }
        }
        return returnTicket;
    }

    @Override
    public <T extends Ticket> T getTicket(final String ticketId, final @NonNull Class<T> clazz) {
        val ticket = getTicket(ticketId);
        if (ticket == null) {

View on GitHub (pinned to e7288fc434)