{"record":{"id":"a6527e59270f6670","repo":"instructure/canvas-lms","slug":"couldn-t-find-qti-migration-tool-see-https-github-com","errorCode":null,"errorMessage":"Couldn't find QTI Migration Tool. See https://github.com/instructure/QTIMigrationTool/wiki for installation instructions.","messagePattern":"Couldn't find QTI Migration Tool\\. See https://github\\.com/instructure/QTIMigrationTool/wiki for installation instructions\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gems/plugins/qti_exporter/lib/qti.rb","lineNumber":129,"sourceCode":"  end\n\n  def self.convert_files(manifest_path)\n    attachments = []\n    doc = Nokogiri::XML(File.open(manifest_path))\n    resource_nodes = doc.css(\"resource\")\n    doc.css(\"file\").each do |file|\n      # skip resource nodes, which are things like xml metadata and other sorts\n      next if resource_nodes.any? { |node| node[\"href\"] == file[\"href\"] }\n\n      # anything left is a file that needs to become an attachment on the context\n      attachments << CGI.unescape(file[\"href\"])\n    end\n    attachments\n  end\n\n  def self.get_conversion_command(out_dir, manifest_file, file_path_prepend = nil)\n    if @migration_executable.nil?\n      raise \"Couldn't find QTI Migration Tool. See https://github.com/instructure/QTIMigrationTool/wiki for installation instructions.\"\n    else\n      prepend = file_path_prepend ? \"--pathprepend=\\\"#{file_path_prepend}\\\" \" : \"\"\n      \"\\\"#{@migration_executable}\\\" #{prepend}--ucvars --nogui --overwrite --cpout=#{Shellwords.escape(out_dir)} #{Shellwords.escape(manifest_file)} 2>&1\"\n    end\n  end\nend\n","sourceCodeStart":111,"sourceCodeEnd":136,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/gems/plugins/qti_exporter/lib/qti.rb#L111-L136","documentation":"The qti_exporter plugin shells out to an external Python QTI Migration Tool binary to convert QTI packages. get_conversion_command raises this when the class variable @migration_executable was never located/initialized. It is a hard dependency failure: exports/conversions cannot proceed without the tool.","triggerScenarios":"Calling QTI.get_conversion_command (or any export flow that reaches it, e.g. course copy/QTI export) when QTI.migration_executable was never set — the tool isn't installed, isn't on PATH, or the lookup in the gem's init failed to find it.","commonSituations":"Fresh dev environments or Docker images without the QTIMigrationTool cloned/built; the tool not added to PATH; upgrading Canvas and skipping the setup doc at the wiki link in the message.","solutions":["Install the QTI Migration Tool per https://github.com/instructure/QTIMigrationTool/wiki and make it executable","Ensure the tool's directory is on PATH so the gem's executable lookup succeeds","Verify in a console that QTI.migration_executable is set after boot","For CI/dev-only environments that never export QTI, guard the export feature behind a feature flag or skip those specs"],"exampleFix":"// before (tool missing)\n$ which migration_tool  # -> not found\n// after\n$ git clone https://github.com/instructure/QTIMigrationTool.git && export PATH=$PATH:/path/to/QTIMigrationTool","handlingStrategy":"validation","validationCode":"raise 'QTI tool not installed' unless defined?(QTI) && QTI.migration_executable.present?","typeGuard":null,"tryCatchPattern":"begin\n  QTI.get_conversion_command(out_dir, manifest)\nrescue RuntimeError => e\n  raise unless e.message.include?('QTI Migration Tool')\n  Rails.logger.error('Install QTIMigrationTool: see wiki')\n  raise ExportSetupError\nend","preventionTips":["Install the tool in every dev image and Dockerfile","Add a boot-time or spec-level check that QTI.migration_executable is set","Document PATH setup in onboarding docs"],"tags":["ruby","qti","missing-binary","external-tool"],"backgroundTag":"missing-dependency","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"}