{"record":{"id":"604440afbc0e08ae","repo":"instructure/canvas-lms","slug":"no-course-id-given-for-a-course","errorCode":null,"errorMessage":"No course_id given for a course","messagePattern":"No course_id given for a course","errorType":"exception","errorClass":"ImportError","httpStatus":null,"severity":"error","filePath":"lib/sis/course_importer.rb","lineNumber":66,"sourceCode":"    class Work\n      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","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/sis/course_importer.rb#L48-L84","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (courses.csv row)\n,term-1,account-1,,active,2026-01-01,2026-05-31,,,,My Course\n// after\ncourse-101,term-1,account-1,,active,2026-01-01,2026-05-31,,,,My Course","handlingStrategy":"validation","validationCode":"row = csv_row\nraise ArgumentError, 'blank course_id' if row['course_id'].to_s.strip.empty?\nimporter.add_course(row['course_id'].strip, ...)","typeGuard":"def valid_course_id?(id) = id.is_a?(String) && !id.strip.empty?","tryCatchPattern":"begin\n  importer.add_course(...)\nrescue SisImport::ImportError => e\n  raise if e.message != 'No course_id given for a course'\n  log.warn(\"skipping course row with blank course_id\")\nend","preventionTips":["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"],"tags":["sis-import","missing-required-field","csv-data"],"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"}