instructure/canvas-lms · error · ImportError
No course_id given for a course
Error message
No course_id given for a course
What it means
SisCourseImporter#add_course raises ImportError when the course_id column value of a SIS courses.csv row is blank. Every course row in a SIS import must identify the course via course_id (the SIS source id) for matching, so an empty value cannot be processed. This is a row-level data validation guard at the very start of add_course, before any database work.
Solutions
- Fix the courses.csv row so course_id is populated with a non-blank unique SIS id, then re-run the import
- Verify CSV column ordering matches the expected courses.csv header so course_id is not parsed from the wrong column
- Filter or reject blank course_id rows upstream in the data export before feeding the SIS import
- Wrap the import batch in error handling that captures ImportError and reports the offending row for correction
Example fix
// before (courses.csv row) ,term-1,account-1,,active,2026-01-01,2026-05-31,,,,My Course // after course-101,term-1,account-1,,active,2026-01-01,2026-05-31,,,,My Course
Defensive patterns
Strategy: validation
Validate before calling
row = csv_row raise ArgumentError, 'blank course_id' if row['course_id'].to_s.strip.empty? importer.add_course(row['course_id'].strip, ...)
Type guard
def valid_course_id?(id) = id.is_a?(String) && !id.strip.empty?
Try / catch
begin
importer.add_course(...)
rescue SisImport::ImportError => e
raise if e.message != 'No course_id given for a course'
log.warn("skipping course row with blank course_id")
end Prevention
- Never hand-edit courses.csv; generate it from a validated export
- Assert CSV column count/order against the header before import
- Reject rows with blank key columns (course_id) in a pre-flight validator
- Watch SIS import error reports after each batch and fix feeds at the source
When it happens
Trigger: Calling SisCourseImporter#add_course with course_id=nil, "", or whitespace-only (Anything blank? -> true). Occurs when a courses.csv row has an empty course_id column, or API/XML SIS import input omits course_id.
Common situations: Hand-edited or exported courses.csv where the first column was accidentally deleted; CSV parsing misalignment shifting columns; template rows with only headers left in the file; automated data feeds exporting blank SIS ids for new records.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- No grade_publishing_status given for enrollment #
- No long_name given for course #
- No name given for # #
- No short_name given for course #
- A course did not pass validation
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/604440afbc0e08ae.
Report an issue: GitHub.
Appendix: source
Thrown at lib/sis/course_importer.rb:66
class Work
attr_accessor :success_count, :roll_back_data
def initialize(batch, root_account, logger, a1, a2, m, batch_user, blueprint_associations)
@batch = batch
@batch_user = batch_user
@root_account = root_account
@courses_to_update_sis_batch_id = a1
@course_ids_to_update_associations = a2
@roll_back_data = []
@blueprint_associations = blueprint_associations
@messages = m
@logger = logger
@success_count = 0
end
def add_course(course_id, term_id, account_id, fallback_account_id, status, start_date, end_date, abstract_course_id, short_name, long_name, integration_id, course_format, blueprint_course_id, grade_passback_setting, homeroom_course, friendly_name)
state_changes = []
raise ImportError, "No course_id given for a course" if course_id.blank?
raise ImportError, "No short_name given for course #{course_id}" if short_name.blank? && abstract_course_id.blank?
raise ImportError, "No long_name given for course #{course_id}" if long_name.blank? && abstract_course_id.blank?
raise ImportError, "Improper status \"#{status}\" for course #{course_id}" unless /\A(active|deleted|completed|unpublished|published)/i.match?(status)
raise ImportError, "Invalid course_format \"#{course_format}\" for course #{course_id}" unless course_format.blank? || course_format =~ /\A(online|on_campus|blended|not_set)/i
valid_grade_passback_settings = %w[nightly_sync disabled not_set]
raise ImportError, "Invalid grade_passback_setting \"#{grade_passback_setting}\" for course #{course_id}" unless grade_passback_setting.blank? || valid_grade_passback_settings.include?(grade_passback_setting.downcase.strip)
return if @batch.skip_deletes? && status =~ /deleted/i
Course.unique_constraint_retry do
course = @root_account.all_courses.find_by(sis_source_id: course_id)
if course.nil?
course = Course.new
state_changes << :created
else
state_changes << :updated
end
course.saved_by = :sis_importView on GitHub (pinned to 1c9f0bb801)