apereo/cas · warning

Ticket passed is null and cannot be decoded

Error message

Ticket passed is null and cannot be decoded

What it means

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.

Solutions

  1. Check where the ticket ID originates (cookie, parameter) and handle the null return instead of assuming a non-null ticket
  2. Verify the ticket still exists in the registry (e.g. ticketRegistry.getTicket(id)) before further processing
  3. 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
  4. If it should never be null, debug why the upstream lookup produced a null ticket value (e.g. broken cookie extraction)

Example fix

// before
Ticket t = ticketRegistry.getTicket(ticketId);
String decoded = ticketRegistry.decodeTicket(t.getValue()); // NPE if t is null
// after
Ticket t = ticketRegistry.getTicket(ticketId);
if (t == null) {
    LOGGER.warn("No ticket found for [{}]", ticketId);
    return null;
}
String decoded = ticketRegistry.decodeTicket(t.getValue());
Defensive patterns

Strategy: type-guard

Validate before calling

if (ticketId == null || ticketId.isBlank()) { throw new IllegalArgumentException("ticket id required"); }

Type guard

Ticket t = ticketRegistry.getTicket(ticketId);
if (t == null || ticketRegistry.decodeTicket(t.getId()) == null) {
    // treat as no-ticket / expired
}

Prevention

When it happens

Trigger: Calling decodeTicket(null) directly; or any registry path (getTicket, deleteTicket, etc.) that passes a null ticket string/object into decodeTicket while isCipherExecutorEnabled() is true.

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

Related errors


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

Appendix: source

Thrown at core/cas-server-core-tickets-api/src/main/java/org/apereo/cas/ticket/registry/AbstractTicketRegistry.java:346

        }
        val encodedTicket = createEncodedTicket(ticket);
        LOGGER.debug("Created encoded ticket [{}]", encodedTicket);
        return encodedTicket;
    }

    protected @Nullable Ticket decodeTicket(final Ticket ticketToProcess) {
        if (ticketToProcess instanceof EncodedTicket && !isCipherExecutorEnabled()) {
            LOGGER.warn("Found removable encoded ticket [{}] yet cipher operations are disabled.", ticketToProcess.getId());
            FunctionUtils.doUnchecked(_ -> deleteTicket(ticketToProcess));
            return null;
        }

        if (!isCipherExecutorEnabled()) {
            LOGGER.trace(TICKET_ENCRYPTION_LOG_MESSAGE);
            return ticketToProcess;
        }
        if (ticketToProcess == null) {
            LOGGER.warn("Ticket passed is null and cannot be decoded");
            return null;
        }
        if (!(ticketToProcess instanceof final EncodedTicket encodedTicket)) {
            LOGGER.debug("Ticket passed is not an encoded ticket: [{}], no decoding is necessary.",
                ticketToProcess.getClass().getSimpleName());
            return ticketToProcess;
        }
        LOGGER.debug("Attempting to decode [{}]", ticketToProcess);
        val ticket = decodeAndDeserialize(encodedTicket.getEncodedTicket());
        LOGGER.debug("Decoded ticket to [{}]", ticket);
        return ticket;
    }

    protected Ticket decodeAndDeserialize(final byte[] encodedTicket) {
        return SerializationUtils.decodeAndDeserializeObject(encodedTicket, this.cipherExecutor, Ticket.class);
    }

    protected Collection<Ticket> decodeTickets(final Collection<Ticket> items) {

View on GitHub (pinned to e7288fc434)