apereo/cas · warning
No expiration policy was found for ticket state
Error message
No expiration policy was found for ticket state [{}]. Consider configuring a predicate that delegates to an expiration policy. What it means
BaseDelegatingExpirationPolicy routes isExpired() checks to a named inner expiration policy chosen by getExpirationPolicyNameFor(ticketState). If no policy is registered under that name (or the name is blank), it logs this warning and falls back to the superclass default behavior rather than throwing. It signals misconfiguration of the delegating policy's predicate/policy map.
Solutions
- Register a policy under BaseDelegatingExpirationPolicy.POLICY_NAME_DEFAULT ("DEFAULT") via addPolicy(...) so unmatched ticket states have a fallback.
- Inspect getExpirationPolicyNameFor() output for a ticket state that matched nothing and fix the predicate/attribute configuration so it returns a registered name.
- Verify the `policies` map contents (they may have been lost via deserialization/JSON binding with Nulls.AS_EMPTY).
- If the fallback is acceptable, silence by ensuring the superclass expiration policy behavior is what you intend; otherwise fix configuration.
Example fix
// before
val policy = new PrincipalAttributeDelegatingExpirationPolicy(...); // only named policies added, no default
// after
policy.addPolicy("DEFAULT", TimeoutExpirationPolicy.builder().build()); // fallback for unmatched states Defensive patterns
Strategy: validation
Validate before calling
assert delegatingPolicy.getPolicies().containsKey("DEFAULT") : "Delegating expiration policy has no DEFAULT fallback policy"; Prevention
- Always register a DEFAULT-named inner policy on any BaseDelegatingExpirationPolicy
- Unit-test the policy predicate against representative authentication attribute sets
- Treat this warn log in production as a config regression signal
When it happens
Trigger: Calling isExpired(ticket) on a delegating expiration policy whose `policies` map lacks an entry for the name returned by getExpirationPolicyNameFor() — e.g. a predicate-based policy builder produced no matching policy for the ticket's authentication/principal attributes.
Common situations: Custom principal-attribute-based expiration policies where the attribute used to pick a policy is missing on the ticket's authentication; policies registered only under non-DEFAULT names while a ticket matches none of the predicates; upgrade where policy registration names changed.
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/8d5bf59bffddf823.
Report an issue: GitHub.
Appendix: source
Thrown at core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/expiration/BaseDelegatingExpirationPolicy.java:83
/**
* Add policy.
*
* @param name the name
* @param policy the policy
* @return the base delegating expiration policy
*/
@CanIgnoreReturnValue
public BaseDelegatingExpirationPolicy addPolicy(final String name, final ExpirationPolicy policy) {
LOGGER.trace("Adding expiration policy [{}] with name [{}]", policy, name);
this.policies.put(name, policy);
return this;
}
@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);View on GitHub (pinned to e7288fc434)