apereo/cas · warning

Unable to determine duration for SAML service

Error message

Unable to determine duration for SAML service [{}] with no entity id

What it means

This is a WARN log, not a thrown error, from SamlRegisteredServiceMetadataExpirationPolicy.getCacheDurationForServiceProvider when the SamlRegisteredService has a blank serviceId (entity ID), making it impossible to resolve an SPSSODescriptor and compute a cache duration. The method returns -1 so the caller falls back to a default duration. It signals a service configuration problem rather than a runtime fault.

Solutions

  1. Set the entityId/serviceId on the SamlRegisteredService (e.g. via the services management UI or service registry JSON) and save.
  2. Audit service registry entries for blank entityId values and repair them.
  3. Fix the import/provisioning pipeline that created the incomplete service record.
  4. If the -1 fallback duration is unacceptable, correct the data before the policy computes durations; no code change is needed once serviceId is set.

Example fix

// before: services/sp-1001.json missing entityId
{
  "@class": "org.apereo.cas.support.saml.services.SamlRegisteredService",
  "name": "SP"
}
// after
{
  "@class": "org.apereo.cas.support.saml.services.SamlRegisteredService",
  "name": "SP",
  "serviceId": "https://sp.example.org/metadata"
}
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the service has a usable entity id before computing durations
if (service == null || org.apache.commons.lang3.StringUtils.isBlank(service.getServiceId())) {
    throw new IllegalArgumentException("SamlRegisteredService requires a non-blank serviceId/entityId");
}

Prevention

When it happens

Trigger: duration()/getCacheDurationForServiceProvider invoked for a service whose getServiceId() is blank or whitespace-only, typically a partially constructed SamlRegisteredService or one loaded from a registry entry missing the entityId field.

Common situations: Service registry JSON/YAML entry missing the entityId/serviceId property; programmatic service registration that never set the entity ID; metadata-driven provisioning that produced an empty entity id after a bad import.

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/49567324faf880b5. 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/SamlRegisteredServiceMetadataExpirationPolicy.java:78

        final long currentTime, final long currentDuration) {
        LOGGER.trace("Cache expiration duration after updates is set to [{}] nanoseconds", currentDuration);
        return currentDuration;
    }

    @Override
    public long expireAfterRead(
        final @NonNull SamlRegisteredServiceCacheKey cacheKey,
        final @NonNull CachedMetadataResolverResult cacheResult,
        final long currentTime, final long currentDuration) {
        LOGGER.trace("Cache expiration duration after reads is set to [{}] nanoseconds", currentDuration);
        return currentDuration;
    }

    long getCacheDurationForServiceProvider(final SamlRegisteredService service,
                                            final CachedMetadataResolverResult cacheResult) {
        try {
            if (StringUtils.isBlank(service.getServiceId())) {
                LOGGER.warn("Unable to determine duration for SAML service [{}] with no entity id", service.getName());
                return -1;
            }
            val set = new CriteriaSet();
            set.add(new EntityIdCriterion(service.getServiceId()));
            set.add(new EntityRoleCriterion(SPSSODescriptor.DEFAULT_ELEMENT_NAME));
            val entitySp = cacheResult.getMetadataResolver().resolveSingle(set);
            if (entitySp != null && entitySp.getCacheDuration() != null) {
                LOGGER.debug("Located cache duration [{}] specified in SP metadata for [{}]", entitySp.getCacheDuration(), entitySp.getEntityID());
                return TimeUnit.MILLISECONDS.toNanos(entitySp.getCacheDuration().toMillis());
            }

            set.clear();
            set.add(new EntityIdCriterion(service.getServiceId()));
            val entity = cacheResult.getMetadataResolver().resolveSingle(set);
            if (entity != null && entity.getCacheDuration() != null) {
                LOGGER.debug("Located cache duration [{}] specified in entity metadata for [{}]", entity.getCacheDuration(), entity.getEntityID());
                return TimeUnit.MILLISECONDS.toNanos(entity.getCacheDuration().toMillis());
            }

View on GitHub (pinned to e7288fc434)