apereo/cas · warning

Ticket created [ ] second(s) in the future. Check time…

Error message

Ticket created [{}] second(s) in the future. Check time synchronization on all servers.

What it means

AbstractTicketRegistry.getTicket computes the ticket's age from its creation timestamp. If the age is less than -1 seconds, the ticket appears to have been created in the future relative to the current node's clock, so it warns that time is not synchronized across servers. Clock skew between CAS nodes (or clients writing ticket timestamps) can break expiration math and throttled-use policies.

Solutions

  1. Enable and verify NTP/chrony time synchronization on all CAS nodes and the container hosts.
  2. Check clock offsets (chronyc tracking / ntpq -p) on the node producing the warning versus siblings.
  3. Ensure containers use the host clock (mount /etc/localtime or use host time sync) and are not drifting.
  4. If skew is unavoidable at the sub-2s level, note the -1 threshold; otherwise treat persistent warnings as a serious cluster-time drift issue because expiration policies depend on wall-clock time.

Example fix

# before: node clocks drifting
# after
sudo apt install chrony && sudo systemctl enable --now chrony
chronyc tracking  # verify offset is <100ms on every CAS node
Defensive patterns

Strategy: validation

Validate before calling

// deployment check: fail health probe if clock offset exceeds threshold
OffsetSeconds offset = readNtpOffset();
if (Math.abs(offset.value) > 1) { throw new IllegalStateException("Clock skew " + offset + "s exceeds 1s; sync NTP"); }

Prevention

When it happens

Trigger: getTicket(id) on a cluster where the node serving the read has a clock behind the node that created the ticket by more than 1 second — ticketAgeSeconds < -1 in getTicket.

Common situations: Multi-node CAS deployments without NTP; VM/container clock drift (paused VMs, Docker on hosts with skewed clocks); load balancer routing a validation request to a lagging node right after ticket creation.

Related errors


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

Appendix: source

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

                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) {
            LOGGER.debug("Ticket [{}] with type [{}] cannot be found", ticketId, clazz.getSimpleName());
            throw new InvalidTicketException(ticketId);
        }
        if (!clazz.isAssignableFrom(ticket.getClass())) {
            throw new ClassCastException("Ticket [" + ticket.getId() + " is of type "
                + ticket.getClass() + " when we were expecting " + clazz);
        }
        return clazz.cast(ticket);
    }

View on GitHub (pinned to e7288fc434)