{"record":{"id":"8d5bf59bffddf823","repo":"apereo/cas","slug":"no-expiration-policy-was-found-for-ticket-state","errorCode":null,"errorMessage":"No expiration policy was found for ticket state [{}]. Consider configuring a predicate that delegates to an expiration policy.","messagePattern":"No expiration policy was found for ticket state \\[(.+?)\\]\\. 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":83,"sourceCode":"    /**\n     * Add policy.\n     *\n     * @param name   the name\n     * @param policy the policy\n     * @return the base delegating expiration policy\n     */\n    @CanIgnoreReturnValue\n    public BaseDelegatingExpirationPolicy addPolicy(final String name, final ExpirationPolicy policy) {\n        LOGGER.trace(\"Adding expiration policy [{}] with name [{}]\", policy, name);\n        this.policies.put(name, policy);\n        return this;\n    }\n\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);","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/expiration/BaseDelegatingExpirationPolicy.java#L65-L101","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nval policy = new PrincipalAttributeDelegatingExpirationPolicy(...); // only named policies added, no default\n// after\npolicy.addPolicy(\"DEFAULT\", TimeoutExpirationPolicy.builder().build()); // fallback for unmatched states","handlingStrategy":"validation","validationCode":"assert delegatingPolicy.getPolicies().containsKey(\"DEFAULT\") : \"Delegating expiration policy has no DEFAULT fallback policy\";","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["cas","expiration-policy","misconfiguration","fallback"],"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"}