apereo/cas · error

INVALID_REQUEST

INVALID_REQUEST

Error message

Could not identify service and/or service ticket for service: [{}]

What it means

In handleRequestInternal of the service validation controller, the request was missing either the service parameter or a service ticket (artifact), so CAS cannot even begin validation. It returns the error view with code INVALID_REQUEST.

Solutions

  1. Ensure the client calls validation with both parameters, e.g. /p3/serviceValidate?service=<registered-url>&ticket=ST-....
  2. Fix the client's callback/service URL so the ticket query parameter is preserved and not truncated (no missing '?', no aggressive URL rewriting).
  3. If using a custom service format, register the appropriate ArgumentExtractor so the service can be extracted from the request.

Example fix

// before
curl 'https://cas.example.org/cas/p3/serviceValidate?ticket=ST-1-abc'

// after
curl 'https://cas.example.org/cas/p3/serviceValidate?service=https%3A%2F%2Fmyapp.example.org%2Flogin&ticket=ST-1-abc'
Defensive patterns

Strategy: validation

Validate before calling

// before calling the CAS validation endpoint
if (!serviceUrl || !ticketId) {
  throw new Error('Both service and ticket parameters are required for CAS validation');
}
const url = `${casBaseUrl}/p3/serviceValidate?service=${encodeURIComponent(serviceUrl)}&ticket=${encodeURIComponent(ticketId)}`;

Try / catch

try {
    // call validation endpoint
} catch (err) {
    // check for INVALID_REQUEST code: missing service/ticket params
}

Prevention

When it happens

Trigger: The argument extractor's extractService(request) returns null, or the extracted service's getArtifactId() is blank — i.e. a GET/POST to /serviceValidate or /p3/serviceValidate without both 'service' and 'ticket' query parameters.

Common situations: Client library dropping the ticket parameter after redirect back; manual curl tests hitting the endpoint without parameters; misconfigured callback URL truncating the query string; custom argument extractor not recognizing the service format.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at support/cas-server-support-validation-core/src/main/java/org/apereo/cas/web/AbstractServiceValidateController.java:92

    protected Ticket handleProxyGrantingTicketDelivery(final String serviceTicketId, final Credential credential) throws Throwable {
        val serviceTicket = serviceValidateConfigurationContext.getTicketRegistry().getTicket(serviceTicketId, ServiceTicket.class);
        val authenticationResult = serviceValidateConfigurationContext.getAuthenticationSystemSupport()
            .finalizeAuthenticationTransaction(serviceTicket.getService(), credential);
        val proxyGrantingTicket = serviceValidateConfigurationContext.getCentralAuthenticationService()
            .createProxyGrantingTicket(serviceTicketId, authenticationResult);
        LOGGER.debug("Generated proxy-granting ticket [{}] off of service ticket [{}] and credential [{}]",
            proxyGrantingTicket.getId(), serviceTicketId, credential);
        return proxyGrantingTicket;
    }

    @Override
    public ModelAndView handleRequestInternal(final HttpServletRequest request,
                                              final HttpServletResponse response) throws Exception {
        val service = serviceValidateConfigurationContext.getArgumentExtractor().extractService(request);
        val serviceTicketId = Optional.ofNullable(service).map(WebApplicationService::getArtifactId).orElse(null);
        if (service == null || StringUtils.isBlank(serviceTicketId)) {
            LOGGER.warn("Could not identify service and/or service ticket for service: [{}]", service);
            return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_REQUEST, StringUtils.EMPTY, request, service);
        }
        try {
            prepareForTicketValidation(request, service, serviceTicketId);
            return handleTicketValidation(request, response, service, serviceTicketId);
        } catch (final AbstractTicketValidationException e) {
            val code = e.getCode();
            val description = getTicketValidationErrorDescription(code,
                new Object[]{serviceTicketId, e.getService().getId(), service.getId()}, request);
            return generateErrorView(code, description, request, service);
        } catch (final AbstractTicketException e) {
            val description = getTicketValidationErrorDescription(e.getCode(), new Object[]{serviceTicketId}, request);
            return generateErrorView(e.getCode(), description, request, service);
        } catch (final UnauthorizedProxyingException e) {
            val description = getTicketValidationErrorDescription(
                CasProtocolConstants.ERROR_CODE_UNAUTHORIZED_SERVICE_PROXY, new Object[]{service.getId()}, request);
            return generateErrorView(CasProtocolConstants.ERROR_CODE_UNAUTHORIZED_SERVICE_PROXY, description, request, service);
        } catch (final UnauthorizedServiceException | PrincipalException e) {

View on GitHub (pinned to e7288fc434)