{"record":{"id":"18a985ecbbd98d47","repo":"apereo/cas","slug":"could-not-locate-sp-ssodescriptor-in-the-metadata","errorCode":null,"errorMessage":"Could not locate SP SSODescriptor in the metadata for [{}]","messagePattern":"Could not locate SP SSODescriptor in the metadata for \\[(.+?)\\]","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"support/cas-server-support-saml-idp-core/src/main/java/org/apereo/cas/support/saml/services/idp/metadata/SamlRegisteredServiceMetadataAdaptor.java","lineNumber":147,"sourceCode":"        final String entityID,\n        final MetadataResolver chainingMetadataResolver,\n        final EntityDescriptor entityDescriptor) {\n        val ssoDescriptor = entityDescriptor.getSPSSODescriptor(SAMLConstants.SAML20P_NS);\n        if (ssoDescriptor != null) {\n            LOGGER.debug(\"Located SP SSODescriptor in metadata for [{}]. Metadata is valid until [{}]\", entityID,\n                ObjectUtils.getIfNull(ssoDescriptor.getValidUntil(), \"forever\"));\n            if (ssoDescriptor.getValidUntil() != null) {\n                val validUntil = DateTimeUtils.zonedDateTimeOf(ssoDescriptor.getValidUntil());\n                val expired = validUntil.isBefore(ZonedDateTime.now(ZoneOffset.UTC));\n                if (expired) {\n                    LOGGER.warn(\"SP SSODescriptor in the metadata has expired at [{}]\", ssoDescriptor.getValidUntil());\n                    return Optional.empty();\n                }\n            }\n            return Optional.of(new SamlRegisteredServiceMetadataAdaptor(ssoDescriptor,\n                entityDescriptor, chainingMetadataResolver));\n        }\n        LOGGER.warn(\"Could not locate SP SSODescriptor in the metadata for [{}]\", entityID);\n        return Optional.empty();\n    }\n\n    public ZonedDateTime getValidUntil() {\n        return DateTimeUtils.zonedDateTimeOf(this.ssoDescriptor.getValidUntil());\n    }\n\n    public Organization getOrganization() {\n        return this.ssoDescriptor.getOrganization();\n    }\n\n    public Signature getSignature() {\n        return this.ssoDescriptor.getSignature();\n    }\n\n    public List<ContactPerson> getContactPersons() {\n        return this.ssoDescriptor.getContactPersons();\n    }","sourceCodeStart":129,"sourceCodeEnd":165,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-saml-idp-core/src/main/java/org/apereo/cas/support/saml/services/idp/metadata/SamlRegisteredServiceMetadataAdaptor.java#L129-L165","documentation":"The EntityDescriptor was resolved successfully, but it contains no SPSSODescriptor role (entityDescriptor.getSPSSODescriptor(...) returned null). getAdaptor() warns and returns Optional.empty() because CAS's IdP requires the SP role descriptor to obtain ACS locations, keys, and protocol support.","triggerScenarios":"Calling get(entityID, ...) where the metadata for the entity defines an IdPSSODescriptor/AttributeAuthorityDescriptor or nothing at all, but no <SPSSODescriptor> — e.g. the metadataLocation points to an IdP's own metadata instead of the SP's.","commonSituations":"Accidentally pointing the CAS SAML service's metadataLocation at an IdP metadata document; metadata for a non-SSO role only (e.g., attribute authority); hand-edited/truncated metadata missing the SPSSODescriptor element; entity is an ECP-only or discovery-registered entity without SP SSO support.","solutions":["Verify the metadataLocation actually points to SP metadata containing <SPSSODescriptor>; if it points at an IdP document, replace it with the SP's metadata.","Check supportedNameIdFormats: getSPSSODescriptor is looked up with the configured format — ensure the SP metadata's NameIDFormat is among the service's supported formats (an exact-format lookup can miss the descriptor).","Regenerate/export complete SP metadata including the SPSSODescriptor with its ACS and keys.","Validate the metadata XML with an XMLOnline validator or OpenSAML tooling to confirm the role descriptor is present.","If the SP only supports another role, register it with the appropriate CAS service type instead of the SAML SP flow."],"exampleFix":"// before\n// metadataLocation: https://idp.example.org/saml/metadata   (IdP metadata, no SPSSODescriptor)\n// after\n// metadataLocation: https://sp.example.org/saml/sp/metadata (contains <SPSSODescriptor>)","handlingStrategy":"validation","validationCode":"var resolver = ...; // resolved chaining resolver\nvar descriptor = resolver.resolveSingle(new CriteriaSet(new EntityIdCriterion(entityID)));\nboolean hasSpRole = descriptor != null\n    && descriptor.getSPSSODescriptor(supportedNameIdFormat) != null;\nif (!hasSpRole) {\n    throw new IllegalStateException(\"Metadata for \" + entityID\n        + \" has no SPSSODescriptor (with format \" + supportedNameIdFormat + \") — is this SP metadata?\");\n}","typeGuard":"boolean isSpMetadata(EntityDescriptor d, String nameIdFormat) {\n    return d != null && d.getSPSSODescriptor(nameIdFormat) != null;\n}","tryCatchPattern":"return adaptorResolver.get(registeredService, entityID)\n    .map(Optional::of)\n    .orElseGet(() -> {\n        log.warn(\"Entity {} lacks SPSSODescriptor; verify metadataLocation points at SP metadata\", entityID);\n        return Optional.empty();\n    });","preventionTips":["Confirm the metadataLocation URL returns SP (not IdP) metadata before registering the service.","Validate metadata structure with OpenSAML tooling, not just well-formedness.","Ensure the service's supported NameID formats overlap with the SP metadata's NameIDFormat values.","Never hand-trim metadata exports; always use the vendor's full-document export."],"tags":["saml","metadata","spssodescriptor","missing-role"],"backgroundTag":"entity-not-found","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}