instructure/canvas-lms · error
Error running python qti converter: #
Error message
Error running python qti converter: #{output} What it means
The QTI module shells out to the python QTI converter and checks $?.exitstatus after conversion. A non-zero exit means the converter failed; the raw converter output is embedded in the raised message so operators can diagnose the underlying python-side failure.
Solutions
- Read the converter output in the error message for the actual python traceback / failure cause.
- Validate the source QTI package (unzip it, check imsmanifest.xml and XML well-formedness) and re-export from the source LMS if corrupt.
- Ensure the python converter's dependencies and correct python version are installed on the job host.
- Retry the migration; if a specific question type crashes the converter, remove or convert that item manually first.
- Check permissions/writability of the temp directories used for the conversion.
Example fix
// before # converter crashes: missing lxml in python env // after (on job host) python3 -m pip install lxml # converter dependency # re-run the QTI import; exit status becomes 0
Defensive patterns
Strategy: try-catch
Validate before calling
# pre-flight: verify converter binary and deps
system('python3', '-c', 'import lxml') or raise 'qti converter python deps missing' Try / catch
begin
questions, assessments = Qti.migrate_tqi(src, dest, opts)
rescue RuntimeError => e
raise unless e.message.start_with?('Error running python qti converter:')
logger.error(e.message) # output contains the python traceback
raise MigrationError, 'QTI conversion failed; see logs for converter output'
end Prevention
- Install and pin the converter's python dependencies on migration job hosts.
- Validate QTI packages (zip integrity, well-formed XML) before importing.
- Capture and persist converter stderr for post-mortems.
- Retry failed imports once before surfacing errors to users.
When it happens
Trigger: Running canvas_migrate_qti (conversion in qti.rb) where the python converter subprocess exits non-zero — invalid/corrupt QTI package input, missing python dependencies for the converter, wrong python version, unreadable source files, or converter crash on a specific item type.
Common situations: Importing a QTI zip exported from another LMS with unsupported question types; converter's python dependencies not installed on a new host; QTI package with malformed XML; disk/permission issues in the temp conversion directories.
Understand the failure class
Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.
Related errors
- Can't export QTI without the python converter tool…
- A cross-listing referenced a non-existent section #
- A deleted cross-listing failed: #
- A new_id, '# ', referenced an existing # and the # with #…
- A new_integration_id, '#
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/a6698456160decb0.
Report an issue: GitHub.
Appendix: source
Thrown at gems/plugins/qti_exporter/lib/qti.rb:107
def self.convert_xml(xml, opts = {})
assessments = nil
questions = nil
Dir.mktmpdir do |dirname|
xml_file = File.join(dirname, opts[:file_name] || "qti.xml")
File.open(xml_file, "w") { |f| f << xml }
# convert to 2.1
dest_dir_2_1 = File.join(dirname, "qti_2_1")
command = Qti.get_conversion_command(dest_dir_2_1, dirname)
output = `#{command}`
if $?.exitstatus == 0
manifest = File.join(dest_dir_2_1, "imsmanifest.xml")
questions = convert_questions(manifest, opts)
assessments = convert_assessments(manifest, opts)
else
raise "Error running python qti converter: #{output}"
end
end
[questions, assessments]
end
def self.convert_files(manifest_path)
attachments = []
doc = Nokogiri::XML(File.open(manifest_path))
resource_nodes = doc.css("resource")
doc.css("file").each do |file|
# skip resource nodes, which are things like xml metadata and other sorts
next if resource_nodes.any? { |node| node["href"] == file["href"] }
# anything left is a file that needs to become an attachment on the context
attachments << CGI.unescape(file["href"])
end
attachments
endView on GitHub (pinned to 1c9f0bb801)