instructure/canvas-lms · error · ImportError

A course did not pass validation

Error message

A course did not pass validation (course: #{course_id} / #{short_name}, error: #{course.errors.full_messages.join(",")})

What it means

SisCourseImporter#add_course raises ImportError when the course record itself fails ActiveRecord validation (course.valid? false) just before save_without_broadcasting!. This is the non-templated counterpart of the templated-course validation error; the embedded course.errors.full_messages list names the specific failing validations.

Solutions

  1. Parse the embedded errors.full_messages to find the exact failing attribute(s)
  2. Correct the offending values in courses.csv (e.g. ensure end_date >= start_date, names within limits) and re-run the batch
  3. Verify the course's term and account associations are valid and present under the root account
  4. Check for concurrent UI modifications to the course that make SIS-applied values invalid; reconcile before re-import

Example fix

// before: errors include "End date must be after the start date"
row = {start_date: "2026-05-31", end_date: "2026-01-01"}
// after
row = {start_date: "2026-01-01", end_date: "2026-05-31"}
Defensive patterns

Strategy: try-catch

Validate before calling

msg = e.message
if (errs = msg[/error: (.*)\)$/, 1])
  log.error("course validation failed: #{errs}")
end

Type guard

nil

Try / catch

begin
  importer.add_course(...)
rescue SisImport::ImportError => e
  raise unless e.message.start_with?('A course did not pass validation')
  sis_batch_errors.record(course_id, e.message) # includes errors.full_messages detail
end

Prevention

When it happens

Trigger: add_course reaches the final save path and course.valid? fails — e.g. invalid start/end dates (end before start), name too long, enrollment term restrictions, account_id nil, or uniqueness/constraint issues surfaced as validation errors.

Common situations: CSV rows with malformed or reversed start/end dates; names exceeding length limits after export transformations; conflicting concurrent edits in the UI leaving the course in a state SIS changes cannot validate against; term or account references failing association validations.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at lib/sis/course_importer.rb:257

              else
                msg = "A (templated) course did not pass validation " \
                      "(course: #{course_id} / #{short_name}, error: " \
                      "#{templated_course.errors.full_messages.join(",")})"
                raise ImportError, msg
              end
            end
            course.sis_batch_id = @batch.id
            if course.valid?
              course_changes = course.changes
              course.save_without_broadcasting!
              auditor_state_changes(course, state_changes, course_changes)
              data = SisBatchRollBackData.build_data(sis_batch: @batch, context: course)
              @roll_back_data << data if data
            else
              msg = "A course did not pass validation " \
                    "(course: #{course_id} / #{short_name}, error: " \
                    "#{course.errors.full_messages.join(",")})"
              raise ImportError, msg
            end
            @course_ids_to_update_associations.add(course.id) if update_account_associations
          else
            @courses_to_update_sis_batch_id << course.id
          end

          if blueprint_course_id && !course.deleted?
            case blueprint_course_id
            when "dissociate"
              MasterCourses::ChildSubscription.active.find_by(child_course_id: course.id)&.destroy
            else
              @blueprint_associations[blueprint_course_id] ||= []
              @blueprint_associations[blueprint_course_id] << course_id
            end
          end

          enrollment_data = course.update_enrolled_users(sis_batch: @batch) if update_enrollments
          course.update_enrollment_states_if_necessary

View on GitHub (pinned to 1c9f0bb801)