{"record":{"id":"0fb049babd8916be","repo":"instructure/canvas-lms","slug":"couldn-t-convert-qti-1-2-to-2-1-see-error-log-qti-error-file","errorCode":null,"errorMessage":"Couldn't convert QTI 1.2 to 2.1, see error log: #{qti_error_file}","messagePattern":"Couldn't convert QTI 1\\.2 to 2\\.1, see error log: #(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/cc/importer/canvas/quiz_converter.rb","lineNumber":54,"sourceCode":"      assessments\n    end\n\n    def run_qti_converter(qti_folder)\n      # convert to 2.1\n      @dest_dir_2_1 = File.join(qti_folder, \"qti_2_1\")\n      return unless File.exist?(qti_folder)\n\n      command = Qti.get_conversion_command(@dest_dir_2_1, qti_folder)\n      logger.debug \"Running migration command: #{command}\"\n      python_std_out = `#{command}`\n\n      if $?.exitstatus == 0\n        @converted = true\n      else\n        make_export_dir\n        qti_error_file = File.join(@base_export_dir, \"qti_conversion_error.log\")\n        File.open(qti_error_file, \"w\") { |f| f << python_std_out }\n        raise \"Couldn't convert QTI 1.2 to 2.1, see error log: #{qti_error_file}\"\n      end\n    end\n\n    def convert_questions\n      raise \"The QTI must be converted to 2.1 before converting to JSON\" unless @converted\n\n      questions = {}\n      begin\n        manifest_file = File.join(@dest_dir_2_1, Qti::Converter::MANIFEST_FILE)\n        questions[:assessment_questions] = Qti.convert_questions(manifest_file, flavor: Qti::Flavors::CANVAS)\n      rescue\n        questions[:qti_error] = \"#{$!}: #{$!.backtrace.join(\"\\n\")}\"\n      end\n      questions\n    end\n\n    def convert_assessments\n      raise \"The QTI must be converted to 2.1 before converting to JSON\" unless @converted","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/cc/importer/canvas/quiz_converter.rb#L36-L72","documentation":"Canvas quiz_converter shells out to a Python QTI conversion utility; run_qti_converter checks $?.exitstatus after the subprocess. A non-zero exit means QTI 1.2 could not be upgraded to QTI 2.1, and the converter raises with the path to the log file where the converter's stdout/stderr was written.","triggerScenarios":"Importing a quiz package with malformed or unsupported QTI 1.2 XML; the python converter exits non-zero, python_std_out is dumped to <base_export_dir>/qti_conversion_error.log, and the raise fires from run_qti_converter (invoked via convert_quizzes during course import).","commonSituations":"Migration imports of quizzes exported by other LMSs or old Canvas exports with QTI quirks; missing/incompatible python QTI converter dependencies in the environment; hand-crafted QTI packages with schema violations.","solutions":["Open the qti_conversion_error.log path given in the message and fix the QTI XML issue it reports","Validate/repair the QTI package with an external QTI validator before importing","Confirm the python QTI converter and its dependencies are installed and runnable in the environment","Convert the QTI to 2.1 yourself ahead of time (e.g. via a prep step) so the 1.2->2.1 conversion path is not needed"],"exampleFix":"// before\n# importing a broken qti.zip directly\n// after\n# pre-validate in a wrapper script\nunless system('qti_validator', 'quiz.zip')\n  puts 'fix QTI per validator output before import'\nend","handlingStrategy":"try-catch","validationCode":"# pre-check QTI validity before import\nraise 'invalid QTI' unless File.exist?(manifest) && xml_valid?(manifest)","typeGuard":null,"tryCatchPattern":"begin\n  converter.convert_quizzes\nrescue RuntimeError => e\n  raise unless e.message.start_with?(\"Couldn't convert QTI 1.2 to 2.1\")\n  log_file = e.message[/error log: (.+)$/, 1]\n  Rails.logger.error(File.read(log_file)) if log_file\nend","preventionTips":["Validate QTI packages before import","Keep the python QTI converter and its dependencies installed","Test imports from each source LMS before bulk migration"],"tags":["qti","migration","external-process"],"backgroundTag":"command-not-found","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"}