apereo/cas · warning

Ticket-granting ticket expiration policy is set to ALWAYS…

Error message

Ticket-granting ticket expiration policy is set to ALWAYS expire tickets.

What it means

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.

Solutions

  1. Set realistic positive values for cas.ticket.tgt.primary.max-time-to-live-in-seconds and time-to-kill-in-seconds.
  2. Check whether a placeholder/env variable (e.g. ${TGT_TTL}) resolves to 0 or empty and fix the value.
  3. Confirm you are editing the correct property namespace (cas.ticket.tgt.primary.*) and not the timeout/remember-me subsections.
  4. Restart and verify the logged policy name is TicketGrantingTicketExpirationPolicy, not AlwaysExpiresExpirationPolicy.

Example fix

// before
cas.ticket.tgt.primary.max-time-to-live-in-seconds=0
cas.ticket.tgt.primary.time-to-kill-in-seconds=0
// after
cas.ticket.tgt.primary.max-time-to-live-in-seconds=${TGT_MAX_TTL:28800}
cas.ticket.tgt.primary.time-to-kill-in-seconds=${TGT_TTK:14400}
Defensive patterns

Strategy: validation

Validate before calling

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"); }

Prevention

When it happens

Trigger: 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.

Common situations: 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').

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/c8d9d6f43b3d33cf. Report an issue: GitHub.

Appendix: source

Thrown at core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/expiration/builder/TicketGrantingTicketExpirationPolicyBuilder.java:77

        LOGGER.debug("Remember me expiration policy is being configured based on hard timeout of [{}] seconds", timeToKillInSeconds);
        val rememberMePolicy = new HardTimeoutExpirationPolicy(timeToKillInSeconds);
        val policy = new RememberMeDelegatingExpirationPolicy();
        policy.addPolicy(RememberMeDelegatingExpirationPolicy.POLICY_NAME_REMEMBER_ME, rememberMePolicy);
        policy.addPolicy(BaseDelegatingExpirationPolicy.POLICY_NAME_DEFAULT, toTicketGrantingTicketExpirationPolicy());
        return policy;
    }

    private ExpirationPolicy toTicketGrantingTicketExpirationPolicy() {
        val tgt = casProperties.getTicket().getTgt();

        if (Beans.isInfinitelyDurable(tgt.getPrimary().getMaxTimeToLiveInSeconds())
            && Beans.isInfinitelyDurable(tgt.getPrimary().getTimeToKillInSeconds())) {
            LOGGER.warn("Primary ticket-granting ticket expiration policy is set to NEVER expire tickets.");
            return NeverExpiresExpirationPolicy.INSTANCE;
        }
        if (Beans.isNeverDurable(tgt.getPrimary().getMaxTimeToLiveInSeconds())
            && Beans.isNeverDurable(tgt.getPrimary().getTimeToKillInSeconds())) {
            LOGGER.warn("Ticket-granting ticket expiration policy is set to ALWAYS expire tickets.");
            return AlwaysExpiresExpirationPolicy.INSTANCE;
        }

        if (StringUtils.isNotBlank(tgt.getTimeout().getMaxTimeToLiveInSeconds())) {
            val seconds = Beans.newDuration(tgt.getTimeout().getMaxTimeToLiveInSeconds()).toSeconds();
            LOGGER.debug("Ticket-granting ticket expiration policy is based on a timeout of [{}] seconds", seconds);
            return new TimeoutExpirationPolicy(seconds);
        }

        if (StringUtils.isNotBlank(tgt.getThrottledTimeout().getTimeInBetweenUsesInSeconds())
            && StringUtils.isNotBlank(tgt.getThrottledTimeout().getTimeToKillInSeconds())) {
            val policy = new ThrottledUseAndTimeoutExpirationPolicy();
            val seconds = Beans.newDuration(tgt.getThrottledTimeout().getTimeToKillInSeconds()).toSeconds();
            val timeInBetweenSeconds = Beans.newDuration(tgt.getThrottledTimeout().getTimeInBetweenUsesInSeconds()).toSeconds();
            policy.setTimeToKillInSeconds(seconds);
            policy.setTimeInBetweenUsesInSeconds(timeInBetweenSeconds);
            LOGGER.debug("Ticket-granting ticket expiration policy is based on throttled timeouts");
            return policy;

View on GitHub (pinned to e7288fc434)