apereo/cas · error · MessageDecodingException
Unable to resolve SP ACS URL for AuthnRequest construction
Error message
Unable to resolve SP ACS URL for AuthnRequest construction
What it means
In the IdP-initiated (unsolicited SSO) flow, the shire parameter is the SP's Assertion Consumer Service URL. extractShire derives it from the shire request parameter or from the registered service's responseLocation/location; if the result is blank the profile cannot construct an AuthnRequest and a MessageDecodingException is thrown.
Solutions
- Include the shire parameter in the unsolicited SSO URL: ?providerId=...&shire=https://sp.example.org/acs.
- Set responseLocation (or location) on the SamlRegisteredService so the ACS URL can be derived.
- Fix the providerId so the correct registered service with a configured location is matched.
- Verify the service is actually found in the service registry (enabled, valid date range).
Example fix
// before <a href="/cas/idp/profile/SAML2/Unsolicited/SSO?providerId=sp.example.org"> // after <a href="/cas/idp/profile/SAML2/Unsolicited/SSO?providerId=sp.example.org&shire=https://sp.example.org/Shibboleth.sso/SAML2/POST">
Defensive patterns
Strategy: validation
Validate before calling
val shire = request.getParameter("shire");
val service = servicesManager.findServiceBy(providerId, SamlRegisteredService.class);
if (StringUtils.isBlank(shire) && (service == null || (StringUtils.isBlank(service.getResponseLocation()) && StringUtils.isBlank(service.getLocation()))))
throw new IllegalArgumentException("shire parameter or service ACS location required"); Type guard
boolean hasAcsLocation(SamlRegisteredService s) { return s != null && (StringUtils.isNotBlank(s.getLocation()) || StringUtils.isNotBlank(s.getResponseLocation())); } Try / catch
try {
return controller.handleUnsolicitedSsoRequest(request, response);
} catch (MessageDecodingException e) {
redirect("/error?reason=missing-shire&providerId=" + providerId);
} Prevention
- Always include shire (ACS URL) in IdP-initiated SSO links.
- Configure responseLocation/location on every registered SAML service.
- Validate the full unsolicited SSO URL template with each SP during onboarding.
- Store canonical SSO link templates per SP in documentation.
When it happens
Trigger: GET/POST to the IdP-initiated endpoint where the shire parameter is absent and the registered SAML service for providerId has neither responseLocation nor location set (or the service itself could not be resolved to produce a location).
Common situations: SP integration bookmark/links missing the shire query parameter; service definition imported without ACS location; typo in providerId matching a service with no location configured.
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
- Unable to resolve service provider assertion consumer…
- Configuration element indicated an entityCertificate, but…
- Unable to determine entity id to fetch metadata via MDQ for
- Missing providerId
- Unable to determine duration for SAML service
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/10caddf0e95f613d.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-saml-idp-web/src/main/java/org/apereo/cas/support/saml/web/idp/profile/SamlIdPInitiatedProfileHandlerController.java:163
return clazz.cast(Objects.requireNonNull(builder).buildObject());
}
protected String extractShire(final HttpServletRequest request, final String providerId,
final SamlRegisteredServiceMetadataAdaptor facade)
throws MessageDecodingException {
var shire = request.getParameter(SamlIdPConstants.SHIRE);
if (StringUtils.isBlank(shire)) {
LOGGER.info("Resolving service provider assertion consumer service URL for [{}] and binding [{}]",
providerId, SAMLConstants.SAML2_POST_BINDING_URI);
val acs = facade.getAssertionConsumerService(SAMLConstants.SAML2_POST_BINDING_URI);
shire = Optional.ofNullable(acs)
.map(service -> StringUtils.isBlank(service.getResponseLocation())
? service.getLocation()
: service.getResponseLocation()).orElse(null);
}
if (StringUtils.isBlank(shire)) {
LOGGER.warn("Unable to resolve service provider assertion consumer service URL for AuthnRequest construction for entityID: [{}]", providerId);
throw new MessageDecodingException("Unable to resolve SP ACS URL for AuthnRequest construction");
}
return shire;
}
protected String extractProviderId(final HttpServletRequest request) throws MessageDecodingException {
val providerId = request.getParameter(SamlIdPConstants.PROVIDER_ID);
if (StringUtils.isBlank(providerId)) {
LOGGER.warn("No providerId parameter given in unsolicited SSO authentication request.");
throw new MessageDecodingException("Missing providerId");
}
return providerId;
}
}
View on GitHub (pinned to e7288fc434)