{"record":{"id":"df7d51bf3491585f","repo":"apereo/cas","slug":"no-expiration-policy-was-found-for-ticket-state-df7d51","errorCode":null,"errorMessage":"No expiration policy was found for ticket state [{}] to calculate time-to-live. Consider configuring a predicate that delegates to an expiration policy.","messagePattern":"No expiration policy was found for ticket state \\[(.+?)\\] to calculate time-to-live\\. Consider configuring a predicate that delegates to an expiration policy\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/expiration/BaseDelegatingExpirationPolicy.java","lineNumber":96,"sourceCode":"\n    @Override\n    public boolean isExpired(final TicketGrantingTicketAwareTicket ticketState) {\n        val match = getExpirationPolicyFor(ticketState);\n        if (match.isEmpty()) {\n            LOGGER.warn(\"No expiration policy was found for ticket state [{}]. \"\n                + \"Consider configuring a predicate that delegates to an expiration policy.\", ticketState);\n            return super.isExpired(ticketState);\n        }\n        val policy = match.get();\n        LOGGER.trace(\"Activating expiration policy [{}] for ticket [{}]\", policy.getName(), ticketState);\n        return policy.isExpired(ticketState);\n    }\n\n    @Override\n    public Long getTimeToLive(final Ticket ticketState) {\n        val match = getExpirationPolicyFor((AuthenticationAwareTicket) ticketState);\n        if (match.isEmpty()) {\n            LOGGER.warn(\"No expiration policy was found for ticket state [{}] to calculate time-to-live. \"\n                + \"Consider configuring a predicate that delegates to an expiration policy.\", ticketState);\n            return super.getTimeToLive(ticketState);\n        }\n        val policy = match.get();\n        LOGGER.trace(\"Getting TTL from policy [{}] for ticket [{}]\", policy.getName(), ticketState);\n        return policy.getTimeToLive(ticketState);\n    }\n\n    @JsonIgnore\n    @Override\n    public Long getTimeToLive() {\n        return this.policies.get(POLICY_NAME_DEFAULT).getTimeToLive();\n    }\n\n    protected Optional<ExpirationPolicy> getExpirationPolicyFor(final AuthenticationAwareTicket ticketState) {\n        val name = getExpirationPolicyNameFor(ticketState);\n        LOGGER.trace(\"Received expiration policy name [{}] to activate\", name);\n        if (StringUtils.isNotBlank(name) && policies.containsKey(name)) {","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/expiration/BaseDelegatingExpirationPolicy.java#L78-L114","documentation":"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.","triggerScenarios":"Calling getTimeToLive(ticket) (or code that computes remaining lifetime) on a BaseDelegatingExpirationPolicy whose getExpirationPolicyNameFor() returns a blank or unregistered name for that ticket.","commonSituations":"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.","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."],"exampleFix":"// before\nreturn super.getTimeToLive(ticketState); // reached because no policy matched\n// after\nval delegating = new PrincipalAttributeDelegatingExpirationPolicy(...);\ndelegating.addPolicy(BaseDelegatingExpirationPolicy.POLICY_NAME_DEFAULT, TimeoutExpirationPolicy.builder().build());","handlingStrategy":"validation","validationCode":"val name = policy.getExpirationPolicyNameFor(state); if (name == null || !policy.getPolicies().containsKey(name)) { /* fix predicate/config before deployment */ }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["cas","expiration-policy","misconfiguration","ttl"],"backgroundTag":"missing-config-value","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}