apereo/cas · warning
No expiration policy was found for ticket state
Error message
No expiration policy was found for ticket state [{}] to calculate time-to-live. Consider configuring a predicate that delegates to an expiration policy. What it means
Same resolution failure as the isExpired variant, but on getTimeToLive(): the delegating policy could not find an inner policy named for the ticket state, so it logs this warning and returns the superclass's default TTL. This means TTL numbers reported for such tickets come from the fallback policy, not a per-state policy.
Solutions
- Add a DEFAULT-named policy via addPolicy(POLICY_NAME_DEFAULT, ...) so TTL resolution always succeeds.
- Fix the predicate in getExpirationPolicyNameFor() so it maps every expected ticket state to a registered policy name.
- Verify registered policy names match exactly (case-sensitive) what the predicate returns.
- Check that the policies map was not emptied by deserialization before use.
Example fix
// before return super.getTimeToLive(ticketState); // reached because no policy matched // after val delegating = new PrincipalAttributeDelegatingExpirationPolicy(...); delegating.addPolicy(BaseDelegatingExpirationPolicy.POLICY_NAME_DEFAULT, TimeoutExpirationPolicy.builder().build());
Defensive patterns
Strategy: validation
Validate before calling
val name = policy.getExpirationPolicyNameFor(state); if (name == null || !policy.getPolicies().containsKey(name)) { /* fix predicate/config before deployment */ } Prevention
- Keep policy-name strings as constants shared between predicate and addPolicy call
- Add a DEFAULT policy so getTimeToLive always resolves to a concrete policy
- Verify policy maps survive serialization/deserialization in your ticket registry
When it happens
Trigger: Calling getTimeToLive(ticket) (or code that computes remaining lifetime) on a BaseDelegatingExpirationPolicy whose getExpirationPolicyNameFor() returns a blank or unregistered name for that ticket.
Common situations: Ticket state whose authentication attributes do not satisfy any configured predicate; policies map missing a DEFAULT entry so TTL reporting is misleading; custom delegating policy subclass returning null names.
Understand the failure class
Background: "missing required config value" errors: why libraries refuse to start when a configuration key is empty, unset, or blank — this error's family across 48 libraries.
Related errors
- No expiration policy was found for ticket state
- No expiration policy could be found by the name
- Expired [ ]: number of seconds since ticket usage time [ ]…
- Primary ticket-granting ticket expiration policy is set to…
- Ticket-granting ticket expiration policy is set to ALWAYS…
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/df7d51bf3491585f.
Report an issue: GitHub.
Appendix: source
Thrown at core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/expiration/BaseDelegatingExpirationPolicy.java:96
@Override
public boolean isExpired(final TicketGrantingTicketAwareTicket ticketState) {
val match = getExpirationPolicyFor(ticketState);
if (match.isEmpty()) {
LOGGER.warn("No expiration policy was found for ticket state [{}]. "
+ "Consider configuring a predicate that delegates to an expiration policy.", ticketState);
return super.isExpired(ticketState);
}
val policy = match.get();
LOGGER.trace("Activating expiration policy [{}] for ticket [{}]", policy.getName(), ticketState);
return policy.isExpired(ticketState);
}
@Override
public Long getTimeToLive(final Ticket ticketState) {
val match = getExpirationPolicyFor((AuthenticationAwareTicket) ticketState);
if (match.isEmpty()) {
LOGGER.warn("No expiration policy was found for ticket state [{}] to calculate time-to-live. "
+ "Consider configuring a predicate that delegates to an expiration policy.", ticketState);
return super.getTimeToLive(ticketState);
}
val policy = match.get();
LOGGER.trace("Getting TTL from policy [{}] for ticket [{}]", policy.getName(), ticketState);
return policy.getTimeToLive(ticketState);
}
@JsonIgnore
@Override
public Long getTimeToLive() {
return this.policies.get(POLICY_NAME_DEFAULT).getTimeToLive();
}
protected Optional<ExpirationPolicy> getExpirationPolicyFor(final AuthenticationAwareTicket ticketState) {
val name = getExpirationPolicyNameFor(ticketState);
LOGGER.trace("Received expiration policy name [{}] to activate", name);
if (StringUtils.isNotBlank(name) && policies.containsKey(name)) {View on GitHub (pinned to e7288fc434)