apereo/cas · warning
Could not grant service ticket
Error message
Could not grant service ticket [{}]. Routing to [{}] What it means
This is a WARN log emitted by the CAS GenerateServiceTicketAction when a service ticket cannot be granted from the presented ticket-granting ticket (TGT). After the failure, the webflow is routed to the 'authenticationFailure' transition, so the login flow terminates with an authentication failure event rather than a service ticket.
Solutions
- Check the accompanying exception message (the {} placeholder shows e.getMessage()) to identify the root cause (expired TGT vs registry error).
- Verify the ticket registry (cas.ticket.registry.*) is up, shared across CAS nodes, and not dropping tickets prematurely.
- Confirm the service parameter matches a registered service in the services registry and that the TGT was issued for the same service context.
- Increase ticket-granting-ticket timeout (cas.ticket.tgt.time-to-kill-in-seconds) if tickets expire mid-flow.
Example fix
// before: TGT expires before ST request cas.ticket.tgt.time-to-kill-in-seconds=10 // after cas.ticket.tgt.time-to-kill-in-seconds=28800
Defensive patterns
Strategy: try-catch
Validate before calling
// before triggering the ST grant
val tgt = ticketRegistrySupport.getTicketRegistry().getTicket(tgtId, TicketGrantingTicket.class);
if (tgt == null || tgt.isExpired()) { return error("expired"); }
if (servicesManager.findServiceBy(service) == null) { return error("unauthorized-service"); } Type guard
function isLiveTgt(t: Ticket | null): t is TicketGrantingTicket {
return t instanceof TicketGrantingTicket && !t.isExpired();
} Try / catch
try {
return grantServiceTicket(authnResult, service, context);
} catch (TicketException | AbstractTicketException e) {
LOGGER.warn("Could not grant service ticket: [{}]", e.getMessage());
return newEvent(CasWebflowConstants.TRANSITION_ID_AUTHENTICATION_FAILURE, e);
} Prevention
- Monitor ticket registry health and eviction metrics across CAS nodes.
- Keep TGT time-to-kill comfortably longer than the maximum SSO session length.
- Always register services in the services registry before enabling clients to hit /login.
- Inspect the exception message embedded in the log ({} placeholder) rather than the warning alone.
When it happens
Trigger: doExecuteInternal catches a throwable raised inside grantServiceTicket (e.g. TicketException from ticketRegistry.addTicket, expired/invalid TGT, service not authorized for the TGT) and logs this message before returning the authentication-failure event.
Common situations: TGT expired or evicted from the ticket registry (short ticket timeout, shared registry like Redis/JDBC flushed); service ID mismatch between the service presented and the one bound to the TGT; ticket registry connectivity problems; service not found/authorized in the services registry.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- No authentication found for ticket
- InvalidTicketException
- Impersonating is not allowed
- Service ticket [ ] issued for service [ ] has already…
- warn(logger, getMessage(throwable), throwable)
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/45da8eebab6ab534.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-actions-core/src/main/java/org/apereo/cas/web/flow/GenerateServiceTicketAction.java:116
val credentials = casWebflowCredentialProvider.extract(context);
val builder = authenticationSystemSupport.establishAuthenticationContextFromInitial(authentication,
credentials.toArray(Credential.EMPTY_CREDENTIALS_ARRAY));
val authenticationResult = builder.build(service);
LOGGER.trace("Built the final authentication result [{}] to grant service ticket to [{}]", authenticationResult, service);
grantServiceTicket(authenticationResult, service, context);
return success();
} catch (final Throwable e) {
if (e instanceof InvalidTicketException) {
LOGGER.debug("CAS has determined ticket-granting ticket [{}] is invalid and must be destroyed", ticketGrantingTicket);
ticketRegistrySupport.getTicketRegistry().deleteTicket(ticketGrantingTicket);
}
if (isGatewayPresent(context)) {
LOGGER.debug("Request indicates that it is gateway. Routing result to [{}] state", CasWebflowConstants.TRANSITION_ID_GATEWAY);
return result(CasWebflowConstants.TRANSITION_ID_GATEWAY);
}
LOGGER.warn("Could not grant service ticket [{}]. Routing to [{}]", e.getMessage(), CasWebflowConstants.TRANSITION_ID_AUTHENTICATION_FAILURE);
return newEvent(CasWebflowConstants.TRANSITION_ID_AUTHENTICATION_FAILURE, e);
}
}
private void grantServiceTicket(final AuthenticationResult authenticationResult,
final Service service,
final RequestContext requestContext) {
serviceTicketAuthorities
.stream()
.sorted(AnnotationAwareOrderComparator.INSTANCE)
.filter(auth -> auth.supports(authenticationResult, service))
.findFirst()
.ifPresent(Unchecked.consumer(auth -> {
if (auth.shouldGenerate(authenticationResult, service)) {
FunctionUtils.doUnchecked(_ -> {
val ticketGrantingTicket = WebUtils.getTicketGrantingTicketId(requestContext);
val serviceTicketId = centralAuthenticationService.grantServiceTicket(ticketGrantingTicket, service, authenticationResult);
WebUtils.putServiceTicketInRequestScope(requestContext, serviceTicketId);View on GitHub (pinned to e7288fc434)