instructure/canvas-lms · error
Must be a single Entity
Error message
Must be a single Entity
What it means
populate_from_metadata_xml raises "Must be a single Entity" when, after parsing, the result is not a SAML2::Entity. A Group (EntitiesDescriptor) whose idp_entity_id is blank, or whose entries do not contain the configured entity_id, cannot be narrowed to one entity.
Solutions
- Provide single-entity metadata, or set idp_entity_id to the exact entityID present inside the aggregate before calling populate_from_metadata_xml
- If the IdP changed its entityID, update the provider's idp_entity_id (and entity_id columns) to the new value
- Re-download metadata directly for the specific entity rather than the whole aggregate
Example fix
# before provider.populate_from_metadata_xml(aggregate_xml) # after provider.idp_entity_id = 'https://idp.example.com/saml/metadata' provider.populate_from_metadata_xml(aggregate_xml)
Defensive patterns
Strategy: validation
Validate before calling
entity = SAML2::Entity.parse(xml)
if entity.is_a?(SAML2::Entity::Group)
raise 'must select one entity' unless provider.idp_entity_id.present?
raise 'entity id not in metadata' unless entity.any? { |e| e.entity_id == provider.idp_entity_id }
end Type guard
->(parsed) { parsed.is_a?(SAML2::Entity) } Try / catch
begin provider.populate_from_metadata_xml(xml) rescue RuntimeError => e raise e unless e.message == 'Must be a single Entity' # prompt admin to pick a single entity from the aggregate end
Prevention
- Keep idp_entity_id in sync when the IdP changes its entityID
- Prefer per-entity metadata over aggregates
- Inspect EntitiesDescriptor contents before importing
When it happens
Trigger: Saving aggregate metadata (EntitiesDescriptor) without idp_entity_id set; metadata Group does not contain an entity matching the provider's idp_entity_id (IdP rotated its entityID); passing multiple entities at once.
Common situations: Admin pastes a federation aggregate; IdP changed its entityID so the stored idp_entity_id no longer matches any entry; new provider created with aggregate metadata and no entity selected.
Related errors
- Must provide exactly one IDPSSODescriptor; found #
- Invalid schema
- A new_id, '# ', referenced an existing # and the # with #…
- A new_integration_id, '#
- A student referenced a non-existent user #
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/8eb8100d92f5aac0.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/authentication_provider/saml.rb:334
when true
# use ||= to not overwrite a specific algorithm that has otherwise been
# chosen
self.sig_alg ||= "RSA-SHA1"
when false
self.sig_alg = nil
# else nil
# don't change the user settings
end
end
def populate_from_metadata_xml(xml, source: "manual")
entity = SAML2::Entity.parse(xml)
raise "Invalid schema" unless entity&.valid_schema?
if entity.is_a?(SAML2::Entity::Group) && idp_entity_id.present?
entity = entity.find { |e| e.entity_id == idp_entity_id }
end
raise "Must be a single Entity" unless entity.is_a?(SAML2::Entity)
populate_from_metadata(entity)
# Only set this after all the above runs so that we catch any issues before overwriting the cached metadata
settings["metadata"] = xml
settings["metadata_source"] = source
end
alias_method :metadata=, :populate_from_metadata_xml
def populate_from_metadata_url(url)
::Canvas.timeout_protection("saml_metadata_fetch") do
CanvasHttp.get(url) do |response|
# raise error unless it's a 2xx
response.value
populate_from_metadata_xml(response.body, source: "url")
end
end
end
View on GitHub (pinned to 1c9f0bb801)