apereo/cas · warning
SP SSODescriptor in the metadata has expired at
Error message
SP SSODescriptor in the metadata has expired at [{}] What it means
Both the EntityDescriptor and its SPSSODescriptor were located, but the SPSSODescriptor's own validUntil timestamp is before now (UTC). getAdaptor() logs a warning and returns Optional.empty(), refusing to hand back an adaptor whose endpoint/key validity window has closed.
Solutions
- Refresh the metadata from the SP so the SPSSODescriptor carries a current validUntil, then reload it in CAS.
- Inspect the XML: check <SPSSODescriptor validUntil=...> (not just <EntityDescriptor>) to see which level expired.
- Switch from a snapshot file to the SP's live metadata URL so refreshes happen automatically.
- Have the SP administrator republish metadata without a restrictive validUntil if the window is unnecessarily short.
- Purge CAS's cached metadata for this service so the renewed document is picked up immediately.
Example fix
// before (in SP metadata) // <SPSSODescriptor validUntil="2024-06-01T00:00:00Z" ...> // after // <SPSSODescriptor validUntil="2030-01-01T00:00:00Z" ...> (or omit validUntil)
Defensive patterns
Strategy: validation
Validate before calling
var descriptor = entityDescriptor != null ? entityDescriptor.getSPSSODescriptor(supportedNameIdFormat) : null;
if (descriptor != null && descriptor.getValidUntil() != null) {
var validUntil = DateTimeUtils.zonedDateTimeOf(descriptor.getValidUntil());
if (validUntil.isBefore(ZonedDateTime.now(ZoneOffset.UTC))) {
throw new IllegalStateException("SPSSODescriptor expired at " + validUntil
+ " — republish SP metadata");
}
} Type guard
boolean ssoDescriptorCurrent(SPSSODescriptor sso) {
if (sso == null || sso.getValidUntil() == null) return true;
return DateTimeUtils.zonedDateTimeOf(sso.getValidUntil()).isAfter(ZonedDateTime.now(ZoneOffset.UTC));
} Try / catch
adaptorResolver.get(registeredService, entityID)
.filter(adaptor -> adaptor.getValidUntil() == null
|| adaptor.getValidUntil().isAfter(ZonedDateTime.now(ZoneOffset.UTC)))
.orElseThrow(() -> new SamlException("SP SSODescriptor expired — refresh metadata for " + entityID)); Prevention
- Check validity at BOTH entity and SPSSODescriptor levels when reviewing metadata XML.
- Have SP admins republish metadata on a schedule matching its declared validity window.
- Prefer metadata without restrictive validUntil for long-lived internal integrations.
- Inspect the adaptor's getValidUntil() in health checks to catch upcoming expiry.
When it happens
Trigger: Calling get(entityID, ...) when the resolved SPSSODescriptor (entityDescriptor.getSPSSODescriptor(supportedNameIdFormat)) has a non-null validUntil earlier than ZonedDateTime.now(ZoneOffset.UTC).
Common situations: SP re-publishes metadata with short validity periods but the IdP cached the old document; a static metadata export was taken days ago and its descriptor-level validUntil has since passed; mismatch between entity-level (fine) and role-descriptor-level (expired) validity, which is easy to miss when eyeballing the XML.
Related errors
- Entity descriptor in the metadata has expired at
- Could not locate SP SSODescriptor in the metadata for
- No assertion consumer service could be found for entity
- Endpoint for is not available or does not define a binding…
- Endpoint for does not define a binding or location for…
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/79d5ded4c930e743.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-saml-idp-core/src/main/java/org/apereo/cas/support/saml/services/idp/metadata/SamlRegisteredServiceMetadataAdaptor.java:140
} catch (final Exception e) {
LoggingUtils.error(LOGGER, e);
}
return Optional.empty();
}
private static Optional<SamlRegisteredServiceMetadataAdaptor> getAdaptor(
final String entityID,
final MetadataResolver chainingMetadataResolver,
final EntityDescriptor entityDescriptor) {
val ssoDescriptor = entityDescriptor.getSPSSODescriptor(SAMLConstants.SAML20P_NS);
if (ssoDescriptor != null) {
LOGGER.debug("Located SP SSODescriptor in metadata for [{}]. Metadata is valid until [{}]", entityID,
ObjectUtils.getIfNull(ssoDescriptor.getValidUntil(), "forever"));
if (ssoDescriptor.getValidUntil() != null) {
val validUntil = DateTimeUtils.zonedDateTimeOf(ssoDescriptor.getValidUntil());
val expired = validUntil.isBefore(ZonedDateTime.now(ZoneOffset.UTC));
if (expired) {
LOGGER.warn("SP SSODescriptor in the metadata has expired at [{}]", ssoDescriptor.getValidUntil());
return Optional.empty();
}
}
return Optional.of(new SamlRegisteredServiceMetadataAdaptor(ssoDescriptor,
entityDescriptor, chainingMetadataResolver));
}
LOGGER.warn("Could not locate SP SSODescriptor in the metadata for [{}]", entityID);
return Optional.empty();
}
public ZonedDateTime getValidUntil() {
return DateTimeUtils.zonedDateTimeOf(this.ssoDescriptor.getValidUntil());
}
public Organization getOrganization() {
return this.ssoDescriptor.getOrganization();
}
View on GitHub (pinned to e7288fc434)