{"record":{"id":"8bd606ac0edf5d7b","repo":"instructure/canvas-lms","slug":"invalid-schema","errorCode":null,"errorMessage":"Invalid schema","messagePattern":"Invalid schema","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/models/authentication_provider/saml.rb","lineNumber":329,"sourceCode":"    end\n\n    settings[:signing_certificates] = idp.signing_keys.filter_map(&:x509)\n    settings[:signing_keys] = idp.signing_keys.filter_map(&:key).map(&:to_s)\n    case idp.want_authn_requests_signed?\n    when true\n      # use ||= to not overwrite a specific algorithm that has otherwise been\n      # chosen\n      self.sig_alg ||= \"RSA-SHA1\"\n    when false\n      self.sig_alg = nil\n      # else nil\n      # don't change the user settings\n    end\n  end\n\n  def populate_from_metadata_xml(xml, source: \"manual\")\n    entity = SAML2::Entity.parse(xml)\n    raise \"Invalid schema\" unless entity&.valid_schema?\n\n    if entity.is_a?(SAML2::Entity::Group) && idp_entity_id.present?\n      entity = entity.find { |e| e.entity_id == idp_entity_id }\n    end\n    raise \"Must be a single Entity\" unless entity.is_a?(SAML2::Entity)\n\n    populate_from_metadata(entity)\n    # Only set this after all the above runs so that we catch any issues before overwriting the cached metadata\n    settings[\"metadata\"] = xml\n    settings[\"metadata_source\"] = source\n  end\n  alias_method :metadata=, :populate_from_metadata_xml\n\n  def populate_from_metadata_url(url)\n    ::Canvas.timeout_protection(\"saml_metadata_fetch\") do\n      CanvasHttp.get(url) do |response|\n        # raise error unless it's a 2xx\n        response.value","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/authentication_provider/saml.rb#L311-L347","documentation":"populate_from_metadata_xml parses the supplied XML with SAML2::Entity.parse and raises \"Invalid schema\" when parsing yields nil or the entity fails valid_schema?. The metadata XML does not conform to SAML metadata XSD.","triggerScenarios":"Calling populate_from_metadata_xml with malformed XML, truncated XML, or well-formed XML that violates SAML metadata schema; also when populate_from_metadata_url downloads non-XML content (HTML error page) and passes it through.","commonSituations":"IdP metadata URL returns an HTML login/error page; XML pasted with copy/paste corruption or extra text; IdP upgraded and publishes schema-invalid metadata; wrong URL used for metadata.","solutions":["Fetch the metadata again from the IdP and confirm it is valid XML before saving","Validate the XML against SAML metadata schema (xmllint with the metadata XSD) to find the offending elements","Check that the metadata URL is the correct SAML metadata endpoint, not an HTML page"],"exampleFix":"# before\nprovider.populate_from_metadata_xml(response.body)\n# after\nentity = SAML2::Entity.parse(response.body)\nraise 'bad metadata' unless entity&.valid_schema?\nprovider.populate_from_metadata_xml(response.body)","handlingStrategy":"validation","validationCode":"xml = URI.parse(metadata_url).read\nraise 'not xml' unless xml.lstrip.start_with?('<?xml', '<')\nraise 'invalid schema' unless SAML2::Entity.parse(xml)&.valid_schema?","typeGuard":"null","tryCatchPattern":"begin\n  provider.populate_from_metadata_url(url)\nrescue RuntimeError => e\n  raise e unless e.message == 'Invalid schema'\n  Rails.logger.error(\"SAML metadata from #{url} is not schema-valid\")\nend","preventionTips":["Verify the metadata URL returns XML, not an HTML error page","Validate metadata against the SAML metadata XSD before saving","Re-download metadata after IdP upgrades"],"tags":["ruby","saml","xml","schema-validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}