{"record":{"id":"7c64a66f2862bdc8","repo":"apereo/cas","slug":"expired-number-of-seconds-since-ticket-usage","errorCode":null,"errorMessage":"Expired [{}]: number of seconds since ticket usage time [{}] is less than or equal to time in between uses in seconds [{}]","messagePattern":"Expired \\[(.+?)\\]: number of seconds since ticket usage time \\[(.+?)\\] is less than or equal to time in between uses in seconds \\[(.+?)\\]","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/expiration/ThrottledUseAndTimeoutExpirationPolicy.java","lineNumber":71,"sourceCode":"\n        LOGGER.trace(\"Current time is [{}]. Ticket last used time is [{}]\", currentTime, lastTimeUsed);\n\n        val margin = Duration.between(lastTimeUsed, currentTime).toSeconds();\n        LOGGER.trace(\"The duration in seconds between current time and last used time is [{}]\", margin);\n\n        if (ticketState.getCountOfUses() == 0 && margin < this.timeToKillInSeconds) {\n            LOGGER.debug(\"Valid [{}]: Usage count is zero and number of seconds since ticket usage time [{}] is less than [{}]\",\n                ticketState, margin, this.timeToKillInSeconds);\n            return super.isExpired(ticketState);\n        }\n\n        if (margin >= this.timeToKillInSeconds) {\n            LOGGER.debug(\"Expired [{}]: number of seconds since ticket usage time [{}] is greater than or equal to [{}]\",\n                ticketState, margin, this.timeToKillInSeconds);\n            return true;\n        }\n        if (margin > 0 && margin <= this.timeInBetweenUsesInSeconds) {\n            LOGGER.warn(\"Expired [{}]: number of seconds since ticket usage time [{}] is less than or equal to time in between uses in seconds [{}]\",\n                ticketState, margin, this.timeInBetweenUsesInSeconds);\n            return true;\n        }\n\n        return super.isExpired(ticketState);\n    }\n\n    @Override\n    public Long getTimeToLive() {\n        return this.timeToKillInSeconds;\n    }\n\n    @Override\n    public Long getTimeToIdle() {\n        return this.timeInBetweenUsesInSeconds;\n    }\n\n    @JsonIgnore","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/expiration/ThrottledUseAndTimeoutExpirationPolicy.java#L53-L89","documentation":"ThrottledUseAndTimeoutExpirationPolicy.isExpired() returns true (ticket expired) when the ticket is being used sooner than `timeInBetweenUsesInSeconds` allows — i.e. the ticket is being used 'too fast', which this policy treats as invalid. This prevents rapid automated replay of tickets while still allowing the ticket to live up to timeToKillInSeconds. The message wording ('Expired ... is less than or equal to time in between uses') is the warn log emitted in that branch.","triggerScenarios":"Validating a ticket whose seconds-since-last-use (margin) is > 0 and <= timeInBetweenUsesInSeconds. E.g. cas.ticket.st.timeToKillInSeconds=10 but timeInBetweenUsesInSeconds also small and the client re-validates within that window.","commonSituations":"Clients validating the same ticket twice in quick succession (the second call); misconfigured `cas.ticket.*.time-in-between-uses-in-seconds` set too high relative to legitimate client behavior; clock skew between CAS nodes making margin compute near zero for a just-used ticket.","solutions":["Understand the ticket is intentionally rejected: obtain a new service ticket and validate only once.","Lower cas.ticket.<type>.time-in-between-uses-in-seconds (or set to 0) if legitimate fast reuse is expected in your flow.","Synchronize clocks (NTP) across CAS cluster nodes to avoid spurious near-zero margins.","If your application needs multiple validations, use a different expiration policy (e.g. TimeoutExpirationPolicy) instead of the throttled one."],"exampleFix":"// before (application.properties)\ncas.ticket.st.time-in-between-uses-in-seconds=5\n// after\ncas.ticket.st.time-in-between-uses-in-seconds=0  # or validate each ST exactly once and discard","handlingStrategy":"validation","validationCode":"// ensure legit reuse interval is larger than the throttle window\ncas.ticket.st.time-in-between-uses-in-seconds=0 # or > max expected client retry delay","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate each service ticket exactly once and discard it","Set time-in-between-uses-in-seconds to 0 unless you deliberately want throttling","Run NTP on all CAS nodes so margin calculations are stable"],"tags":["cas","expiration-policy","throttling","replay-protection"],"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"}