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
- Enable and verify NTP/chrony time synchronization on all CAS nodes and the container hosts.
- Check clock offsets (chronyc tracking / ntpq -p) on the node producing the warning versus siblings.
- Ensure containers use the host clock (mount /etc/localtime or use host time sync) and are not drifting.
- 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
- Run chrony/NTP on every CAS node and container host
- Include clock-offset checks in health endpoints or readiness probes
- After VM resume or host maintenance, verify clock resync before resuming traffic
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
- Principal attribute [
- Token has expired: and is after
- Token cannot be used before
- Dn format cannot be empty/blank for authentication
- Proof iat is in the future
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)