spring-projects/spring-security · error · IllegalArgumentException
metadataResolver must be an IterableMetadataSource or have…
Error message
metadataResolver must be an IterableMetadataSource or have a RoleMetadataIndex
What it means
BaseOpenSamlAssertingPartyMetadataRepository's constructor requires the supplied resolver adapter to either expose a RoleMetadataIndex (allowing role-based entity lookups) or be an IterableMetadataSource (allowing plain iteration). Neither being true makes the repository unable to enumerate IDP EntityDescriptors, so it rejects the configuration with this IllegalArgumentException.
Solutions
- Add a RoleMetadataIndex to your resolver: metadataResolver.setIndexes(Set.of(new RoleMetadataIndex())) before initialize()
- Or implement/wrap the resolver as an IterableMetadataSource that can iterate all entities
- Use one of Spring Security's provided adapters (e.g. ResourceBackedMetadataResolver via initialize()) instead of a custom one
- Review the repository Javadoc/constructor for the accepted resolver shapes
Example fix
// before
var adapter = new MetadataResolverAdapter(myPlainResolver) {};
// after
myPlainResolver.setIndexes(Set.of(new RoleMetadataIndex()));
myPlainResolver.initialize();
var adapter = new MetadataResolverAdapter(myPlainResolver) {}; Defensive patterns
Strategy: validation
Validate before calling
void requireUsableResolver(MetadataResolver r) {
boolean roleIndexed = r != null && r.getIndexes() != null
&& r.getIndexes().stream().anyMatch(RoleMetadataIndex.class::isInstance);
boolean iterable = r instanceof IterableMetadataSource;
if (!roleIndexed && !iterable) {
throw new IllegalArgumentException("resolver needs RoleMetadataIndex or IterableMetadataSource");
}
} Try / catch
try {
var repo = new ResourceBackedAssertingPartyMetadataRepository(resource);
} catch (Saml2Exception | IllegalArgumentException ex) {
logger.error("Bad metadata resolver configuration: {}", ex.getMessage(), ex);
throw ex;
} Prevention
- Always set Set.of(new RoleMetadataIndex()) on custom resolvers passed to these repositories
- Prefer Spring Security's own ResourceBackedMetadataResolver/initialize() helper over raw OpenSAML resolvers
- Unit-test repository construction with your resolver at startup (fail fast)
When it happens
Trigger: Constructing BaseOpenSamlAssertingPartyMetadataRepository (or a subclass like the file-system/JDBC variants) with a MetadataResolverAdapter whose metadataResolver is not role-indexed and does not implement IterableMetadataSource.
Common situations: Passing a custom OpenSAML MetadataResolver (e.g. a plain FilesystemMetadataResolver or DynamicEntityMetadataResolver) into a resource-backed repository without wrapping it or adding a RoleMetadataIndex; upgrading Spring Security where repository construction API changed; hand-rolling the adapter for tests.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Can not set rememberMeCookieName and custom…
- Failed to parse address 'X'
- invalid_destination
- Metadata response is missing the necessary IDPSSODescriptor…
- registration not found
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/9f621134d352981c.
Report an issue: GitHub.
Appendix: source
Thrown at saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/registration/BaseOpenSamlAssertingPartyMetadataRepository.java:68
* Construct an {@link BaseOpenSamlAssertingPartyMetadataRepository} using the
* provided {@link MetadataResolver}.
*
* <p>
* The {@link MetadataResolver} should either be of type
* {@link IterableMetadataSource} or it should have a {@link RoleMetadataIndex}
* configured.
* @param metadataResolver the {@link MetadataResolver} to use
*/
BaseOpenSamlAssertingPartyMetadataRepository(MetadataResolverAdapter metadataResolver) {
Assert.notNull(metadataResolver, "metadataResolver cannot be null");
if (isRoleIndexed(metadataResolver.metadataResolver)) {
this.descriptors = this::allIndexedEntities;
}
else if (metadataResolver.metadataResolver instanceof IterableMetadataSource source) {
this.descriptors = source::iterator;
}
else {
throw new IllegalArgumentException(
"metadataResolver must be an IterableMetadataSource or have a RoleMetadataIndex");
}
this.metadataResolver = metadataResolver;
}
private static boolean isRoleIndexed(MetadataResolver resolver) {
if (!(resolver instanceof AbstractBatchMetadataResolver batch)) {
return false;
}
for (MetadataIndex index : batch.getIndexes()) {
if (index instanceof RoleMetadataIndex) {
return true;
}
}
return false;
}
private Iterator<EntityDescriptor> allIndexedEntities() {View on GitHub (pinned to 96852e8860)