{"record":{"id":"0018ff29028f9248","repo":"instructure/canvas-lms","slug":"no-short-name-given-for-course-course-id","errorCode":null,"errorMessage":"No short_name given for course #{course_id}","messagePattern":"No short_name given for course #(.+?)","errorType":"exception","errorClass":"ImportError","httpStatus":null,"severity":"error","filePath":"lib/sis/course_importer.rb","lineNumber":67,"sourceCode":"      attr_accessor :success_count, :roll_back_data\n\n      def initialize(batch, root_account, logger, a1, a2, m, batch_user, blueprint_associations)\n        @batch = batch\n        @batch_user = batch_user\n        @root_account = root_account\n        @courses_to_update_sis_batch_id = a1\n        @course_ids_to_update_associations = a2\n        @roll_back_data = []\n        @blueprint_associations = blueprint_associations\n        @messages = m\n        @logger = logger\n        @success_count = 0\n      end\n\n      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)\n        state_changes = []\n        raise ImportError, \"No course_id given for a course\" if course_id.blank?\n        raise ImportError, \"No short_name given for course #{course_id}\" if short_name.blank? && abstract_course_id.blank?\n        raise ImportError, \"No long_name given for course #{course_id}\" if long_name.blank? && abstract_course_id.blank?\n        raise ImportError, \"Improper status \\\"#{status}\\\" for course #{course_id}\" unless /\\A(active|deleted|completed|unpublished|published)/i.match?(status)\n        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\n\n        valid_grade_passback_settings = %w[nightly_sync disabled not_set]\n        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)\n        return if @batch.skip_deletes? && status =~ /deleted/i\n\n        Course.unique_constraint_retry do\n          course = @root_account.all_courses.find_by(sis_source_id: course_id)\n          if course.nil?\n            course = Course.new\n            state_changes << :created\n          else\n            state_changes << :updated\n          end\n          course.saved_by = :sis_import\n          course_enrollment_term_id_stuck = course.stuck_sis_fields.include?(:enrollment_term_id)","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/sis/course_importer.rb#L49-L85","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nCOURSE; \"course-101\",term-1,account-1,,active,,,,,, \"Long Name Only\"\n// after (add short_name)\n\"course-101\",term-1,account-1,,active,,,,,,\"CS101\",\"Long Name Only\"","handlingStrategy":"validation","validationCode":"if row['short_name'].to_s.strip.empty? && row['abstract_course_id'].to_s.strip.empty?\n  raise ArgumentError, \"course #{row['course_id']} needs short_name or abstract_course_id\"\nend","typeGuard":"def has_name_identity?(row) = !row['short_name'].to_s.strip.empty? || !row['abstract_course_id'].to_s.strip.empty?","tryCatchPattern":"begin\n  importer.add_course(...)\nrescue SisImport::ImportError => e\n  raise unless e.message.start_with?('No short_name given')\n  log.warn(\"row missing short_name/abstract_course_id: #{e.message}\")\nend","preventionTips":["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"],"tags":["sis-import","missing-required-field","course-import"],"backgroundTag":"empty-required-field","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"}