instructure/canvas-lms · error · ImportError

A (templated) course did not pass validation

Error message

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

What it means

SisCourseImporter#add_course raises ImportError when a templated (blueprint/linked) course fails ActiveRecord validation before save_without_broadcasting!. The message includes course_id, short_name, and the concatenated validation errors from templated_course.errors.full_messages, so the real cause is in the embedded error list.

Solutions

  1. Read the embedded errors.full_messages in the message to identify the exact failed validation
  2. Fix the offending course attribute in courses.csv (name length, dates, format) and re-import
  3. Check whether the blueprint/master course was altered in the UI in a way that now blocks SIS updates; reconcile it first
  4. Update Canvas if a model validation regression is rejecting previously valid SIS data

Example fix

// before: message shows errors like "Name is too long" for a 300-char name
short_name = very_long_exported_code
// after
short_name = very_long_exported_code[0, 255]
Defensive patterns

Strategy: try-catch

Validate before calling

msg = e.message
if (errs = msg[/error: (.*)\)$/, 1])
  log.error("templated 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 (templated) course did not pass validation')
  sis_batch_errors.record(course_id, e.message) # surface embedded errors.full_messages to data owners
end

Prevention

When it happens

Trigger: add_course path where the course is a blueprint/templated course and templated_course.valid? fails after applying SIS changes (e.g. invalid name length, invalid dates, constraint violations on the child/templated course object).

Common situations: Blueprint course structures where the master course was modified outside SIS making child updates invalid; name/title exceeding column or validation limits; changes conflicting with master-course restrictions; data from the CSV violating model validations only visible on the templated copy.

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/e9c4321e2f2e2384. Report an issue: GitHub.

Appendix: source

Thrown at lib/sis/course_importer.rb:243

              templated_course.name = course.name if !templated_course.stuck_sis_fields.include?(:name) && !course_name_stuck
              templated_course.course_code = course.course_code if !templated_course.stuck_sis_fields.include?(:course_code) && !course_course_code_stuck
              templated_course.enrollment_term = course.enrollment_term if !templated_course.stuck_sis_fields.include?(:enrollment_term_id) && !course_enrollment_term_id_stuck
              if !templated_course.stuck_sis_fields.intersect?(%i[start_at conclude_at restrict_enrollments_to_course_dates]) && !course_dates_stuck
                templated_course.start_at = course.start_at
                templated_course.conclude_at = course.conclude_at
                templated_course.restrict_enrollments_to_course_dates = course.restrict_enrollments_to_course_dates
              end
              templated_course.sis_batch_id = @batch.id
              @course_ids_to_update_associations.add(templated_course.id) if templated_course.account_id_changed? || templated_course.root_account_id_changed?
              if templated_course.valid?
                changes = templated_course.changes
                templated_course.save_without_broadcasting!
                Auditors::Course.record_updated(templated_course, @batch_user, changes, source: :sis, sis_batch_id: @batch_id)
              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

View on GitHub (pinned to 1c9f0bb801)