apereo/cas · error · SamlException

No metadata resolvers could be configured for service with…

Error message

No metadata resolvers could be configured for service  with metadata location 

What it means

SamlRegisteredServiceMetadataResolverCacheLoader.load() is the LoadingCache loader producing a CachedMetadataResolverResult for a SamlRegisteredServiceCacheKey. If loadMetadataResolvers() returns an empty list — no configured resolver (resource/HTTP/file/db/etc.) applies to or succeeds for the service — it throws SamlException naming the service and its resolved metadata location.

Solutions

  1. Set a valid metadataLocation on the registered service (e.g. file:/etc/cas/metadata/sp-metadata.xml or https://sp.example.com/metadata) and save it.
  2. Confirm the scheme of metadataLocation matches an available resolver (HTTP, filesystem, classpath, JDBC, MongoDB, etc.) and the corresponding CAS module is included in the build.
  3. Check that any SpEL in metadataLocation actually resolves to a non-empty value (log/resolve it manually with SpringExpressionLanguageValueResolver).
  4. Verify metadata resolution for that location manually (curl/file read) to rule out an empty or unreachable source.

Example fix

// before (service registered without location)
{ "name": "SP", "serviceId": "^https://sp.example.com", "metadataLocation": "" }

// after
{ "name": "SP", "serviceId": "^https://sp.example.com", "metadataLocation": "https://sp.example.com/saml/metadata" }
Defensive patterns

Strategy: validation

Validate before calling

// when creating/updating a SAML registered service
String loc = SpringExpressionLanguageValueResolver.getInstance().resolve(service.getMetadataLocation());
if (loc == null || loc.isBlank())
    throw new IllegalArgumentException("Service " + service.getName() + " needs a concrete metadataLocation");
if (!(loc.startsWith("http") || loc.startsWith("file") || loc.startsWith("classpath")))
    throw new IllegalArgumentException("Unsupported metadataLocation scheme: " + loc);

Try / catch

try {
    CachedMetadataResolverResult r = cache.get(cacheKey);
} catch (SamlException e) {
    logger.error("Service {} metadata misconfigured: {}", service.getName(), e.getMessage());
}

Prevention

When it happens

Trigger: load() (public) calls loadMetadataResolvers(cacheKey); the result is empty because the registered service's metadataLocation is blank/unresolvable after Spring EL resolution, or no SamlRegisteredServiceMetadataResolver in the chain matches/supports the location scheme.

Common situations: Registered service created without a metadataLocation; metadataLocation uses an unsupported scheme (e.g. missing http(s)/file prefix) or a SpEL expression that resolves to empty; metadata resource module not on the classpath so no resolver is registered.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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

Appendix: source

Thrown at support/cas-server-support-saml-idp-metadata/src/main/java/org/apereo/cas/support/saml/services/idp/metadata/cache/SamlRegisteredServiceMetadataResolverCacheLoader.java:45

 * @since 5.0.0
 */
@Slf4j
@RequiredArgsConstructor
public class SamlRegisteredServiceMetadataResolverCacheLoader implements CacheLoader<SamlRegisteredServiceCacheKey, CachedMetadataResolverResult> {

    protected final OpenSamlConfigBean configBean;

    protected final HttpClient httpClient;
    
    private final SamlRegisteredServiceMetadataResolutionPlan metadataResolutionPlan;

    @Override
    public CachedMetadataResolverResult load(final SamlRegisteredServiceCacheKey cacheKey) {
        val metadataResolvers = loadMetadataResolvers(cacheKey);
        if (metadataResolvers.isEmpty()) {
            val registeredService = cacheKey.getRegisteredService();
            val metadataLocation = SpringExpressionLanguageValueResolver.getInstance().resolve(registeredService.getMetadataLocation());
            throw new SamlException("No metadata resolvers could be configured for service " + registeredService.getName()
                + " with metadata location " + metadataLocation);
        }

        val metadataResolver = initializeChainingMetadataResolver(metadataResolvers);
        LOGGER.debug("Metadata resolvers active for this request are [{}]", Objects.requireNonNull(metadataResolvers));
        return CachedMetadataResolverResult
            .builder()
            .cachedInstant(Instant.now(Clock.systemUTC()))
            .metadataResolver(metadataResolver)
            .build();
    }

    protected MetadataResolver initializeChainingMetadataResolver(final List<MetadataResolver> metadataResolvers) {
        return FunctionUtils.doUnchecked(() -> {
            val metadataResolver = new ChainingMetadataResolver();
            metadataResolver.setId(ChainingMetadataResolver.class.getCanonicalName());
            LOGGER.trace("There are [{}] eligible metadata resolver(s) for this request", metadataResolvers.size());
            metadataResolver.setResolvers(metadataResolvers);

View on GitHub (pinned to e7288fc434)