instructure/canvas-lms · error · ImportError
No short_name given for course #
Error message
No short_name given for course #{course_id} What it means
SisCourseImporter#add_course raises ImportError when short_name is blank AND abstract_course_id is also blank. A course must have either a short_name (course code) or be linked to an existing abstract course; with neither, the importer cannot build a valid course record.
Solutions
- Populate the short_name column (course code, e.g. "CS101") in the courses.csv row
- Alternatively supply abstract_course_id so the course links to an existing abstract course instead of needing a short_name
- Validate the CSV before import: reject rows where both short_name and abstract_course_id are blank
- If updating an existing course, confirm the importer run is not wiping short_name due to a data feed regression
Example fix
// before COURSE; "course-101",term-1,account-1,,active,,,,,, "Long Name Only" // after (add short_name) "course-101",term-1,account-1,,active,,,,,,"CS101","Long Name Only"
Defensive patterns
Strategy: validation
Validate before calling
if row['short_name'].to_s.strip.empty? && row['abstract_course_id'].to_s.strip.empty?
raise ArgumentError, "course #{row['course_id']} needs short_name or abstract_course_id"
end Type guard
def has_name_identity?(row) = !row['short_name'].to_s.strip.empty? || !row['abstract_course_id'].to_s.strip.empty?
Try / catch
begin
importer.add_course(...)
rescue SisImport::ImportError => e
raise unless e.message.start_with?('No short_name given')
log.warn("row missing short_name/abstract_course_id: #{e.message}")
end Prevention
- Always export short_name even for update-only batches
- Treat short_name+abstract_course_id as a mutually-required pair in export templates
- Add a lint step on the CSV before every import
- Document required courses.csv columns for data producers
When it happens
Trigger: add_course called with short_name=nil/""/whitespace while abstract_course_id is also nil/"" — i.e., a courses.csv row missing both the short_name column and the abstract_course_id column.
Common situations: Export pipelines that only include names for new courses but leave short_name blank on updates; teams assuming long_name alone suffices; abstract-course linking workflows where the abstract_course_id column is dropped during CSV transformation.
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 long_name given for course #
- Improper status "# " for course #
- Invalid course_format "#
- Invalid grade_passback_setting "#
- No course_id given for a course
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/0018ff29028f9248.
Report an issue: GitHub.
Appendix: source
Thrown at lib/sis/course_importer.rb:67
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_import
course_enrollment_term_id_stuck = course.stuck_sis_fields.include?(:enrollment_term_id)View on GitHub (pinned to 1c9f0bb801)