instructure/canvas-lms · error · ImportError

An institutional tag category did not pass validation

Error message

An institutional tag category did not pass validation (category: #{sis_id}, error: #{category.errors.full_messages.join(",")})

What it means

Raised by SisImports::InstitutionalTagCategoryImporter#add_institutional_tag_category when the InstitutionalTagCategory record fails ActiveRecord save/validation. Unlike the argument checks, this happens after the fields pass the blank checks — a model-level validation (e.g. name uniqueness, sis_source_id constraints, account presence) rejected the save. The message includes the sis_id and the joined model error messages.

Solutions

  1. Read the interpolated error text (category.errors.full_messages) — it names the failing validation, e.g. 'Name has already been taken'.
  2. Deduplicate names/sis_source_ids in the institutional_tag_categories CSV for the root account.
  3. Verify the pre-existing record is findable (same root_account_id and sis_source_id) so the importer updates it instead of creating a duplicate.
  4. After code/validations changed, confirm InstitutionalTagCategory satisfies them, then re-run the import.

Example fix

// before (CSV)
cat-1,Department Tags,desc,active
cat-2,Department Tags,desc,active
// after
cat-1,Department Tags,desc,active
cat-2,Course Tags,desc,active
Defensive patterns

Strategy: try-catch

Validate before calling

# Pre-check uniqueness against the model before import
names = rows.map { |r| r[:name] }
duplicates = names.tally.select { |_, c| c > 1 }.keys
raise ArgumentError, "duplicate category names: #{duplicates.join(',')}" unless duplicates.empty?

Try / catch

begin
  importer.add_institutional_tag_category(sis_id, name, description, status)
rescue SisImports::ImportError => e
  # e.message contains category.errors.full_messages
  Rails.logger.error("Validation failed: #{e.message}")
end

Prevention

When it happens

Trigger: Saving a category whose name/root_account/sis_source_id violates a model validation — most commonly a duplicate name or duplicate sis_source_id for the same root_account; missing account association; a validation added by a plugin/patch or version upgrade.

Common situations: Re-importing a CSV where two rows share the same category name under one root account; an old sis_source_id colliding with an existing record not matched by find_by (e.g. different root_account/shard); new validations on InstitutionalTagCategory introduced by an upgrade.

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

Appendix: source

Thrown at lib/sis/institutional_tag_category_importer.rb:66

        category = InstitutionalTagCategory.find_by(root_account_id: @root_account.id, sis_source_id: sis_id)
        category ||= InstitutionalTagCategory.new(account: @root_account,
                                                  root_account: @root_account,
                                                  sis_source_id: sis_id)

        category.name = name
        category.description = description
        category.sis_batch_id = @batch.id
        category.workflow_state = /deleted/i.match?(status) ? "deleted" : "active"

        if category.save
          data = SisBatchRollBackData.build_data(sis_batch: @batch, context: category)
          @roll_back_data << data if data
          @success_count += 1
          maybe_write_roll_back_data
        else
          msg = "An institutional tag category did not pass validation " \
                "(category: #{sis_id}, error: #{category.errors.full_messages.join(",")})"
          raise ImportError, msg
        end
      end

      private

      def maybe_write_roll_back_data
        return if @roll_back_data.count <= 1000

        SisBatchRollBackData.bulk_insert_roll_back_data(@roll_back_data)
        @roll_back_data = []
      end
    end
  end
end

View on GitHub (pinned to 1c9f0bb801)