{"record":{"id":"89b27929c10603cd","repo":"instructure/canvas-lms","slug":"invalid-xml-configuration","errorCode":null,"errorMessage":"Invalid XML Configuration","messagePattern":"Invalid XML Configuration","errorType":"exception","errorClass":"CCImportError","httpStatus":null,"severity":"error","filePath":"lib/cc/importer/blti_converter.rb","lineNumber":113,"sourceCode":"          if (tool[:assignment_points_possible] = ext[:custom_fields].delete(\"outcome\"))\n            tool[:assignment_points_possible] = tool[:assignment_points_possible].to_f\n          end\n          tool[:settings] = ext[:custom_fields]\n        else\n          tool[:extensions] << ext\n        end\n      end\n      if (icon = get_node_val(doc, \"#{link_css_path} > #{blti}|icon\"))\n        tool[:settings] ||= {}\n        tool[:settings][:icon_url] = icon.strip\n      end\n      tool\n    end\n\n    def convert_blti_xml(xml)\n      doc = create_xml_doc(xml)\n      unless doc.namespaces.to_s.downcase.include? \"imsglobal\"\n        raise CCImportError, I18n.t(\"Invalid XML Configuration\")\n      end\n\n      begin\n        tool = convert_blti_link(doc)\n        check_for_unescaped_url_properties(tool) if tool\n      rescue Nokogiri::XML::XPath::SyntaxError\n        raise CCImportError, I18n.t(:invalid_xml_syntax, \"Invalid xml syntax\")\n      end\n      tool\n    end\n\n    def check_for_unescaped_url_properties(obj)\n      # Recursively look for properties named 'url'\n      case obj\n      when Hash\n        obj.select { |k, v| k.to_s == \"url\" && v.is_a?(String) }\n           .each_value { |v| check_for_unescaped_url(v) }\n        obj.each_value { |v| check_for_unescaped_url_properties(v) }","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/cc/importer/blti_converter.rb#L95-L131","documentation":"convert_blti_xml validates that the parsed BLTI/LTI configuration XML carries an imsglobal namespace before importing. If the parsed document's namespaces do not include 'imsglobal', the XML is not recognized as a valid LTI tool configuration and a CCImportError is raised.","triggerScenarios":"Calling convert_blti_xml (directly or via retrieve_and_convert_blti_url) with XML whose root/declared namespaces lack 'imsglobal' — e.g. plain XML, wrong namespace URI, or an HTML error page that happens to parse as XML.","commonSituations":"A tool provider URL returns a non-LTI XML document or error page; the config XML was hand-edited dropping the xmlns declaration; the vendor uses an outdated/incorrect namespace; the fetched content is a login/redirect page.","solutions":["Inspect the fetched XML and ensure the root element declares an imsglobal namespace (e.g. <basic_lti_link xmlns=\"http://www.imsglobal.org/xsd/imslticc_v1p0\">)","Fetch the configuration XML manually (curl) and confirm the tool provider is returning real LTI config, not an error/login page","Obtain a fresh/correct configuration XML URL from the tool vendor"],"exampleFix":"# before (no namespace)\n<cartridge_basiclti_link>\n  ...\n</cartridge_basiclti_link>\n\n# after\n<cartridge_basiclti_link xmlns=\"http://www.imsglobal.org/xsd/imslticc_v1p0\"\n    xmlns:blti=\"http://www.imsglobal.org/xsd/imsbasiclti_v1p0\">\n  ...\n</cartridge_basiclti_link>","handlingStrategy":"validation","validationCode":"doc = Nokogiri::XML(xml)\nraise CCImportError, 'not LTI config' unless doc.namespaces.any? { |_, v| v.downcase.include?('imsglobal') }","typeGuard":"def lti_config_xml?(xml)\n  doc = Nokogiri::XML(xml) { |c| c.norecover }\n  doc.namespaces.values.join.downcase.include?('imsglobal')\nrescue Nokogiri::XML::SyntaxError\n  false\nend","tryCatchPattern":"begin\n  tool = converter.convert_blti_xml(xml)\nrescue CCImportError => e\n  Rails.logger.warn(\"BLTI import rejected: #{e.message}\")\n  flash[:error] = I18n.t('invalid_tool_config')\nend","preventionTips":["Validate the namespace before calling convert_blti_xml","Prefer obtaining config XML via the vendor's official config URL","Check response content-type before treating an HTTP body as LTI XML"],"tags":["xml","lti","import","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"}