{"record":{"id":"a38006d570cfe251","repo":"instructure/canvas-lms","slug":"no-abstract-course-id-given-for-an-abstract-course","errorCode":null,"errorMessage":"No abstract_course_id given for an abstract course","messagePattern":"No abstract_course_id given for an abstract course","errorType":"exception","errorClass":"ImportError","httpStatus":null,"severity":"error","filePath":"lib/sis/abstract_course_importer.rb","lineNumber":49,"sourceCode":"      SisBatchRollBackData.bulk_insert_roll_back_data(importer.roll_back_data)\n\n      importer.success_count\n    end\n\n    class Work\n      attr_accessor :success_count, :abstract_courses_to_update_sis_batch_id, :roll_back_data\n\n      def initialize(batch, root_account, logger)\n        @batch = batch\n        @root_account = root_account\n        @abstract_courses_to_update_sis_batch_id = []\n        @roll_back_data = []\n        @logger = logger\n        @success_count = 0\n      end\n\n      def add_abstract_course(abstract_course_id, short_name, long_name, status, term_id = nil, account_id = nil, fallback_account_id = nil)\n        raise ImportError, \"No abstract_course_id given for an abstract course\" if abstract_course_id.blank?\n        raise ImportError, \"No short_name given for abstract course #{abstract_course_id}\" if short_name.blank?\n        raise ImportError, \"No long_name given for abstract course #{abstract_course_id}\" if long_name.blank?\n        raise ImportError, \"Improper status \\\"#{status}\\\" for abstract course #{abstract_course_id}\" unless /\\Aactive|\\Adeleted/i.match?(status)\n        return if @batch.skip_deletes? && status =~ /deleted/i\n\n        course = AbstractCourse.find_by(root_account_id: @root_account, sis_source_id: abstract_course_id)\n        course ||= AbstractCourse.new\n        unless course.stuck_sis_fields.include?(:enrollment_term_id)\n          course.enrollment_term = @root_account.enrollment_terms.find_by(sis_source_id: term_id) || @root_account.default_enrollment_term\n        end\n        course.root_account = @root_account\n\n        account = nil\n        account = @root_account.all_accounts.find_by(sis_source_id: account_id) if account_id.present?\n        account ||= @root_account.all_accounts.find_by(sis_source_id: fallback_account_id) if fallback_account_id.present?\n        course.account = account if account\n        course.account ||= @root_account\n","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/lib/sis/abstract_course_importer.rb#L31-L67","documentation":"SIS::AbstractCourseImporter#add_abstract_course validates its inputs before touching the database. An abstract course row without an abstract_course_id (the SIS external identifier) cannot be keyed, so the importer raises ImportError immediately. This is a defensive check for malformed SIS CSV/data feed rows.","triggerScenarios":"Calling add_abstract_course(nil/'', ...) — e.g. a SIS courses.csv row missing the course_id column, or a custom importer invocation omitting the first argument (lib/sis/abstract_course_importer.rb:49).","commonSituations":"Malformed SIS export files with empty course_id cells; CSV column misalignment after schema changes; scripts building import rows that skip blank ids.","solutions":["Fix the SIS data source so every abstract course row has a non-blank course_id.","Check CSV parsing/column mapping — a shifted column can blank out course_id.","Filter or report invalid rows upstream before invoking the importer."],"exampleFix":"// before\nimporter.add_abstract_course(nil, 'CS101', 'Intro CS', 'active')\n// after\nraise 'missing id' if row['course_id'].blank?\nimporter.add_abstract_course(row['course_id'], 'CS101', 'Intro CS', 'active')","handlingStrategy":"validation","validationCode":"# Ruby: caller-side guard before invoking the importer\nraise 'blank abstract_course_id' if row['course_id'].to_s.strip.empty?\nimporter.add_abstract_course(row['course_id'], row['short_name'], row['long_name'], row['status'])","typeGuard":null,"tryCatchPattern":"begin\n  importer.add_abstract_course(id, short, long, status)\nrescue SIS::AbstractCourseImporter::ImportError => e\n  warnings << \"Skipping abstract course: #{e.message}\"\nend","preventionTips":["Validate SIS CSV required columns (course_id, short_name, long_name, status) before import.","Check column mappings whenever the source schema changes.","Skip-and-log blank-id rows instead of feeding them to the importer."],"tags":["sis","import","validation","missing-field"],"backgroundTag":"missing-required-argument","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"}