{"record":{"id":"719280615a2d2444","repo":"apereo/cas","slug":"ticket-passed-is-null-and-cannot-be-decoded","errorCode":null,"errorMessage":"Ticket passed is null and cannot be decoded","messagePattern":"Ticket passed is null and cannot be decoded","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":346,"sourceCode":"        }\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);\n        val ticket = decodeAndDeserialize(encodedTicket.getEncodedTicket());\n        LOGGER.debug(\"Decoded ticket to [{}]\", ticket);\n        return ticket;\n    }\n\n    protected Ticket decodeAndDeserialize(final byte[] encodedTicket) {\n        return SerializationUtils.decodeAndDeserializeObject(encodedTicket, this.cipherExecutor, Ticket.class);\n    }\n\n    protected Collection<Ticket> decodeTickets(final Collection<Ticket> items) {","sourceCodeStart":328,"sourceCodeEnd":364,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/registry/AbstractTicketRegistry.java#L328-L364","documentation":"AbstractTicketRegistry.decodeTicket() receives a null ticket when the registry has a cipher executor (encryption/signing) enabled. Instead of throwing, it logs a warning and returns null so callers must handle a null result. It indicates that a null was passed into the decode path, usually because the ticket lookup upstream returned nothing.","triggerScenarios":"Calling decodeTicket(null) directly; or any registry path (getTicket, deleteTicket, etc.) that passes a null ticket string/object into decodeTicket while isCipherExecutorEnabled() is true.","commonSituations":"Requesting a ticket ID that no longer exists (expired or single-use and already consumed); client passing an empty/missing TGT cookie; replication lag between clustered ticket registries returning null.","solutions":["Check where the ticket ID originates (cookie, parameter) and handle the null return instead of assuming a non-null ticket","Verify the ticket still exists in the registry (e.g. ticketRegistry.getTicket(id)) before further processing","If nulls are expected to be common, confirm decodeTicket's null contract is handled at every call site rather than treating the warn as a bug","If it should never be null, debug why the upstream lookup produced a null ticket value (e.g. broken cookie extraction)"],"exampleFix":"// before\nTicket t = ticketRegistry.getTicket(ticketId);\nString decoded = ticketRegistry.decodeTicket(t.getValue()); // NPE if t is null\n// after\nTicket t = ticketRegistry.getTicket(ticketId);\nif (t == null) {\n    LOGGER.warn(\"No ticket found for [{}]\", ticketId);\n    return null;\n}\nString decoded = ticketRegistry.decodeTicket(t.getValue());","handlingStrategy":"type-guard","validationCode":"if (ticketId == null || ticketId.isBlank()) { throw new IllegalArgumentException(\"ticket id required\"); }","typeGuard":"Ticket t = ticketRegistry.getTicket(ticketId);\nif (t == null || ticketRegistry.decodeTicket(t.getId()) == null) {\n    // treat as no-ticket / expired\n}","tryCatchPattern":null,"preventionTips":["Always null-check the result of ticket lookup before decoding","Handle expired/consumed tickets as a normal control-flow case, not an exception","In clustered deployments, account for replication lag when a ticket ID is present on one node only"],"tags":["tickets","null-value","decryption","registry"],"backgroundTag":"null-argument","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"}