{"record":{"id":"c8d9d6f43b3d33cf","repo":"apereo/cas","slug":"ticket-granting-ticket-expiration-policy-is-set-to","errorCode":null,"errorMessage":"Ticket-granting ticket expiration policy is set to ALWAYS expire tickets.","messagePattern":"Ticket-granting ticket expiration policy is set to ALWAYS expire tickets\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/expiration/builder/TicketGrantingTicketExpirationPolicyBuilder.java","lineNumber":77,"sourceCode":"        LOGGER.debug(\"Remember me expiration policy is being configured based on hard timeout of [{}] seconds\", timeToKillInSeconds);\n        val rememberMePolicy = new HardTimeoutExpirationPolicy(timeToKillInSeconds);\n        val policy = new RememberMeDelegatingExpirationPolicy();\n        policy.addPolicy(RememberMeDelegatingExpirationPolicy.POLICY_NAME_REMEMBER_ME, rememberMePolicy);\n        policy.addPolicy(BaseDelegatingExpirationPolicy.POLICY_NAME_DEFAULT, toTicketGrantingTicketExpirationPolicy());\n        return policy;\n    }\n\n    private ExpirationPolicy toTicketGrantingTicketExpirationPolicy() {\n        val tgt = casProperties.getTicket().getTgt();\n\n        if (Beans.isInfinitelyDurable(tgt.getPrimary().getMaxTimeToLiveInSeconds())\n            && Beans.isInfinitelyDurable(tgt.getPrimary().getTimeToKillInSeconds())) {\n            LOGGER.warn(\"Primary ticket-granting ticket expiration policy is set to NEVER expire tickets.\");\n            return NeverExpiresExpirationPolicy.INSTANCE;\n        }\n        if (Beans.isNeverDurable(tgt.getPrimary().getMaxTimeToLiveInSeconds())\n            && Beans.isNeverDurable(tgt.getPrimary().getTimeToKillInSeconds())) {\n            LOGGER.warn(\"Ticket-granting ticket expiration policy is set to ALWAYS expire tickets.\");\n            return AlwaysExpiresExpirationPolicy.INSTANCE;\n        }\n\n        if (StringUtils.isNotBlank(tgt.getTimeout().getMaxTimeToLiveInSeconds())) {\n            val seconds = Beans.newDuration(tgt.getTimeout().getMaxTimeToLiveInSeconds()).toSeconds();\n            LOGGER.debug(\"Ticket-granting ticket expiration policy is based on a timeout of [{}] seconds\", seconds);\n            return new TimeoutExpirationPolicy(seconds);\n        }\n\n        if (StringUtils.isNotBlank(tgt.getThrottledTimeout().getTimeInBetweenUsesInSeconds())\n            && StringUtils.isNotBlank(tgt.getThrottledTimeout().getTimeToKillInSeconds())) {\n            val policy = new ThrottledUseAndTimeoutExpirationPolicy();\n            val seconds = Beans.newDuration(tgt.getThrottledTimeout().getTimeToKillInSeconds()).toSeconds();\n            val timeInBetweenSeconds = Beans.newDuration(tgt.getThrottledTimeout().getTimeInBetweenUsesInSeconds()).toSeconds();\n            policy.setTimeToKillInSeconds(seconds);\n            policy.setTimeInBetweenUsesInSeconds(timeInBetweenSeconds);\n            LOGGER.debug(\"Ticket-granting ticket expiration policy is based on throttled timeouts\");\n            return policy;","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/expiration/builder/TicketGrantingTicketExpirationPolicyBuilder.java#L59-L95","documentation":"The mirror case of error 405: when both primary TGT maxTimeToLiveInSeconds and timeToKillInSeconds are 'never durable' (e.g. 0), the builder warns that every TGT will ALWAYS expire and returns AlwaysExpiresExpirationPolicy.INSTANCE. Tickets granted under this policy are effectively unusable/immediately expired.","triggerScenarios":"Setting both cas.ticket.tgt.primary.max-time-to-live-in-seconds and time-to-kill-in-seconds to a 'never durable' value (e.g. 0) so toTicketGrantingTicketExpirationPolicy() builds AlwaysExpiresExpirationPolicy.","commonSituations":"Typo'd zeros in place of seconds; property placeholder resolving to empty/0; unit confusion (writing milliseconds where seconds are expected, e.g. 0 with intent 'instant').","solutions":["Set realistic positive values for cas.ticket.tgt.primary.max-time-to-live-in-seconds and time-to-kill-in-seconds.","Check whether a placeholder/env variable (e.g. ${TGT_TTL}) resolves to 0 or empty and fix the value.","Confirm you are editing the correct property namespace (cas.ticket.tgt.primary.*) and not the timeout/remember-me subsections.","Restart and verify the logged policy name is TicketGrantingTicketExpirationPolicy, not AlwaysExpiresExpirationPolicy."],"exampleFix":"// before\ncas.ticket.tgt.primary.max-time-to-live-in-seconds=0\ncas.ticket.tgt.primary.time-to-kill-in-seconds=0\n// after\ncas.ticket.tgt.primary.max-time-to-live-in-seconds=${TGT_MAX_TTL:28800}\ncas.ticket.tgt.primary.time-to-kill-in-seconds=${TGT_TTK:14400}","handlingStrategy":"validation","validationCode":"long ttl = Long.parseLong(env.getProperty(\"cas.ticket.tgt.primary.max-time-to-live-in-seconds\", \"28800\")); if (ttl <= 0) { fail(\"TGT max-time-to-live must be > 0\"); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Verify env/placeholder values resolve to positive numbers before startup","Watch startup logs for the ALWAYS/NEVER expire warnings and treat them as launch blockers","Do not confuse milliseconds with seconds in cas.ticket.*.properties"],"tags":["cas","expiration-policy","configuration","security"],"backgroundTag":"invalid-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"}