{"record":{"id":"8d04c4fe45fcc165","repo":"apereo/cas","slug":"found-removable-encoded-ticket-yet-cipher-ope","errorCode":null,"errorMessage":"Found removable encoded ticket [{}] yet cipher operations are disabled.","messagePattern":"Found removable encoded ticket \\[(.+?)\\] yet cipher operations are disabled\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/registry/AbstractTicketRegistry.java","lineNumber":336,"sourceCode":"    }\n\n    protected @Nullable Ticket encodeTicket(final Ticket ticket) throws Exception {\n        if (!isCipherExecutorEnabled()) {\n            LOGGER.trace(TICKET_ENCRYPTION_LOG_MESSAGE);\n            return ticket;\n        }\n        if (ticket == null) {\n            LOGGER.debug(\"Ticket passed is null and cannot be encoded\");\n            return null;\n        }\n        val encodedTicket = createEncodedTicket(ticket);\n        LOGGER.debug(\"Created encoded ticket [{}]\", encodedTicket);\n        return encodedTicket;\n    }\n\n    protected @Nullable Ticket decodeTicket(final Ticket ticketToProcess) {\n        if (ticketToProcess instanceof EncodedTicket && !isCipherExecutorEnabled()) {\n            LOGGER.warn(\"Found removable encoded ticket [{}] yet cipher operations are disabled.\", ticketToProcess.getId());\n            FunctionUtils.doUnchecked(_ -> deleteTicket(ticketToProcess));\n            return null;\n        }\n\n        if (!isCipherExecutorEnabled()) {\n            LOGGER.trace(TICKET_ENCRYPTION_LOG_MESSAGE);\n            return ticketToProcess;\n        }\n        if (ticketToProcess == null) {\n            LOGGER.warn(\"Ticket passed is null and cannot be decoded\");\n            return null;\n        }\n        if (!(ticketToProcess instanceof final EncodedTicket encodedTicket)) {\n            LOGGER.debug(\"Ticket passed is not an encoded ticket: [{}], no decoding is necessary.\",\n                ticketToProcess.getClass().getSimpleName());\n            return ticketToProcess;\n        }\n        LOGGER.debug(\"Attempting to decode [{}]\", ticketToProcess);","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/registry/AbstractTicketRegistry.java#L318-L354","documentation":"AbstractTicketRegistry.decodeTicket detects an EncodedTicket in the registry while the registry's ticket cipher executor is disabled (isCipherExecutorEnabled() == false). Because the ticket cannot be decoded, it is treated as garbage: it is deleted and null is returned (caller sees 'ticket not found'). The warning points at an encryption configuration mismatch over the ticket registry's lifetime.","triggerScenarios":"cas.ticket.crypto.enabled was switched from true to false (or the crypto signing/encryption keys/config changed) while encoded tickets from the previous configuration still exist in the shared registry; decodeTicket then deletes each such ticket on read.","commonSituations":"Rolling upgrades where some nodes encrypt tickets and others have crypto disabled; reverting a ticket-encryption rollout without flushing the ticket store; shared Redis/Memcached/JDBC registry containing pre-change encoded tickets.","solutions":["Flush/clear the ticket registry when toggling cas.ticket.crypto.enabled so no stale EncodedTicket entries remain (users simply log in again).","Keep ticket crypto configuration consistent across ALL nodes in the cluster during rolling upgrades; enable/disable everywhere at once behind a maintenance window.","Re-enable the cipher executor if disabling was unintentional, restoring the previous crypto (signing/encryption) keys so existing encoded tickets decode.","If the warnings are transient after a migration, they are self-healing: each warning deletes one stale ticket; monitor until it stops."],"exampleFix":"// before (rolling restart with mixed config)\nnode1: cas.ticket.crypto.enabled=false\nnode2: cas.ticket.crypto.enabled=true\n// after: uniform config + registry flush before restart\nall nodes: cas.ticket.crypto.enabled=false\n# then: FLUSHDB the redis ticket registry (or equivalent) before serving traffic","handlingStrategy":"fallback","validationCode":"if (!registry.isCipherExecutorEnabled()) { /* ensure registry was flushed of EncodedTicket entries before switching crypto off */ }","typeGuard":"boolean needsDecodeCheck = ticket instanceof EncodedTicket && !registry.isCipherExecutorEnabled();","tryCatchPattern":"// decodeTicket returns null for stale encoded tickets; caller must handle null\nTicket t = ticketRegistry.getTicket(id);\nif (t == null) { /* force re-login */ }","preventionTips":["Keep cas.ticket.crypto.enabled identical on all nodes; never mix enabled/disabled nodes","Flush the shared ticket registry when toggling ticket encryption or rotating keys","Plan crypto changes behind a maintenance window and monitor for this warn during rollout"],"tags":["cas","ticket-registry","encryption","configuration-drift"],"backgroundTag":"incompatible-source-type","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"}