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
- Ensure the client calls validation with both parameters, e.g. /p3/serviceValidate?service=<registered-url>&ticket=ST-....
- Fix the client's callback/service URL so the ticket query parameter is preserved and not truncated (no missing '?', no aggressive URL rewriting).
- 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
- Always URL-encode service and ticket values in the validation request.
- Verify the CAS callback URL preserves its full query string through redirects and proxies.
- Use a maintained CAS client library instead of hand-built validation URLs.
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
- Service [ ] is not found in service registry.
- INVALID_PROXY_CALLBACK
- Service ticket [ ] does not satisfy validation…
- Missing providerId
- No wctx parameter is found
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)