apereo/cas · error · SamlException
Unable to determine entity id to fetch metadata via MDQ for
Error message
Unable to determine entity id to fetch metadata via MDQ for
What it means
Thrown before issuing an MDQ query when no entityID can be determined for the request. The resolver takes the EntityIdCriterion from the criteria set, falling back to the registered service's serviceId; if both are blank there is nothing to query.
Solutions
- Set the serviceId (entityID) on the SamlRegisteredService definition.
- Include an EntityIdCriterion in the criteria set: new CriteriaSet(new EntityIdCriterion(entityId)).
- Fix service registry import/export so the service has a non-blank serviceId.
- Log the service definition to confirm which registered service has the blank id.
Example fix
// before
val criteriaSet = new CriteriaSet(new EntityIdCriterion(""));
// after
val criteriaSet = new CriteriaSet(new EntityIdCriterion("https://sp.example.org/metadata")); Defensive patterns
Strategy: validation
Validate before calling
val entityId = Optional.ofNullable(criteriaSet.get(EntityIdCriterion.class))
.map(EntityIdCriterion::getEntityId).orElseGet(service::getServiceId);
if (StringUtils.isBlank(entityId)) throw new IllegalArgumentException("Register service with non-blank serviceId before MDQ resolution"); Type guard
boolean hasEntityId(SamlRegisteredService s) { return s != null && StringUtils.isNotBlank(s.getServiceId()); } Try / catch
try {
return mdqResolver.resolve(criteriaSet);
} catch (SamlException e) {
LOGGER.error("No entityID for service [{}]", service.getName(), e);
return Set.of();
} Prevention
- Always set serviceId to the SP entityID when registering SAML services.
- Validate service registry entries after import/migration.
- Include EntityIdCriterion explicitly when building criteria sets programmatically.
- Add a startup check that logs SAML services with blank serviceIds.
When it happens
Trigger: Calling getMetadataLocationsForService with a criteria set lacking EntityIdCriterion AND a SamlRegisteredService whose getServiceId() is blank/empty (e.g. service defined with empty serviceId pattern).
Common situations: SAML service registered without a serviceId/entityID; programmatic criteria built without EntityIdCriterion builder; regex-based service whose serviceId does not carry the entityID; import/migration left the field empty.
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
- Unable to get entity from MDQ server and a backup file does…
- Skipped registration of
- Configuration element indicated an entityCertificate, but…
- No assertion consumer service could be found for entity
- Endpoint for is not available or does not define a binding…
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/55f5790087ccd5b1.
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/resolver/MetadataQueryProtocolMetadataResolver.java:121
.proxyUrl(service.getMetadataProxyLocation())
.build();
val response = HttpUtils.execute(exec);
if (response == null || HttpStatus.resolve(response.getCode()).is5xxServerError()) {
LOGGER.error("Unable to fetch metadata from [{}]", metadataLocation);
throw UnauthorizedServiceException.denied("Rejected: %s".formatted(metadataLocation));
}
return response;
}
@Override
protected Set<String> getMetadataLocationsForService(final SamlRegisteredService service, final CriteriaSet criteriaSet) {
LOGGER.trace("Getting metadata location dynamically for [{}] based on criteria [{}]", service.getName(), criteriaSet);
val entityIdCriteria = criteriaSet.get(EntityIdCriterion.class);
val entityId = Optional.ofNullable(entityIdCriteria)
.map(EntityIdCriterion::getEntityId)
.orElseGet(service::getServiceId);
if (StringUtils.isBlank(entityId)) {
throw new SamlException("Unable to determine entity id to fetch metadata via MDQ for " + service.getName());
}
val locations = super.getMetadataLocationsForService(service, criteriaSet);
return locations
.stream()
.map(location -> location.replace("{0}", EncodingUtils.urlEncode(entityId)))
.collect(Collectors.toCollection(LinkedHashSet::new));
}
private static void setFileAttribute(final HttpResponse response, final File backupFile) {
FunctionUtils.doAndHandle(t -> {
val path = backupFile.toPath();
val etag = response.getFirstHeader("ETag").getValue();
Files.setAttribute(path, "user:ETag", ByteBuffer.wrap(etag.getBytes(StandardCharsets.UTF_8)));
});
}
}
View on GitHub (pinned to e7288fc434)