instructure/canvas-lms · error · ImportError

Invalid grade_passback_setting "#

Error message

Invalid grade_passback_setting "#{grade_passback_setting}" for course #{course_id}

What it means

SisCourseImporter#add_course raises ImportError when grade_passback_setting is non-blank and its downcased/stripped value is not in [nightly_sync, disabled, not_set]. This optional column controls the course's gradebook pass-back behavior and must use a recognized value.

Solutions

  1. Use one of exactly: nightly_sync, disabled, not_set (case/whitespace are normalized for you)
  2. Replace "enabled" with "nightly_sync" — the most common near-miss value
  3. Leave the column blank if the course should keep its default behavior
  4. Add pre-import normalization mapping synonyms (on/off/enabled/none) to the three valid values

Example fix

// before
passback = "enabled"
// after
passback = {"enabled"=>"nightly_sync", "on"=>"nightly_sync", "off"=>"disabled", "none"=>"not_set"}.fetch(passback.to_s.strip.downcase, passback)
Defensive patterns

Strategy: validation

Validate before calling

gp = row['grade_passback_setting'].to_s.strip.downcase
raise ArgumentError, "bad grade_passback_setting" unless gp.empty? || %w[nightly_sync disabled not_set].include?(gp)

Type guard

def valid_passback?(v) = v.to_s.strip.empty? || %w[nightly_sync disabled not_set].include?(v.to_s.strip.downcase)

Try / catch

begin
  importer.add_course(...)
rescue SisImport::ImportError => e
  raise unless e.message.start_with?('Invalid grade_passback_setting')
  log.warn("fixing grade_passback_setting for course: #{e.message}")
end

Prevention

When it happens

Trigger: add_course called with grade_passback_setting like "enabled", "sync", "on", "nightly" — anything outside the three-value list after downcase+strip. Note: unlike status/format checks, this one strips and downcases first, so "Nightly_Sync " is fine but "enabled" is not.

Common situations: Feeds exporting "enabled"/"disabled" boolean-ish wording ("enabled" is invalid; the valid inverse is "disabled"); older SIS files written before the setting existed using different vocabulary; docs confusion between "nightly_sync" and "nightly".

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/230b93472e26146e. Report an issue: GitHub.

Appendix: source

Thrown at lib/sis/course_importer.rb:73

        @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)
          if !course_enrollment_term_id_stuck && term_id
            term = @root_account.enrollment_terms.active.find_by(sis_source_id: term_id)
          end
          course.enrollment_term = term if term
          course.root_account = @root_account

View on GitHub (pinned to 1c9f0bb801)