instructure/canvas-lms · error · RuntimeError

must call `load_tags!` first

Error message

must call `load_tags!` first

What it means

MasterCourses::TagHelper caches content tags in an in-memory index built by load_tags!. cached_content_tag_for looks up that index; if load_tags! was never called, @content_tag_index is nil and it raises 'must call `load_tags!` first'.

Solutions

  1. Call load_tags! on the helper before any cached_content_tag_for lookup
  2. Reuse the migration's existing tag_helper instance (e.g. via the migration object) instead of creating a new one
  3. In tests, call load_tags! in a before-hook

Example fix

// before
helper = MasterCourses::TagHelper.new(migration)
tag = helper.cached_content_tag_for(assignment)
// after
helper = MasterCourses::TagHelper.new(migration)
helper.load_tags!
tag = helper.cached_content_tag_for(assignment)
Defensive patterns

Strategy: validation

Validate before calling

helper.load_tags! if helper.instance_variable_get(:@content_tag_index).nil?

Type guard

def tags_loaded?(helper)
  !helper.instance_variable_get(:@content_tag_index).nil?
end

Try / catch

begin
  tag = helper.cached_content_tag_for(content)
rescue RuntimeError => e
  raise unless e.message.include?('load_tags!')
  helper.load_tags!
  tag = helper.cached_content_tag_for(content)
end

Prevention

When it happens

Trigger: Calling helper.cached_content_tag_for(content) on a fresh MasterCourses::TagHelper instance without first calling load_tags! for the relevant migration/master course.

Common situations: Custom migration plugins using the helper directly; tests instantiating TagHelper manually; calling the lookup before the helper is initialized for the current migration step.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at app/models/master_courses/tag_helper.rb:82

      tag
    else
      content_tags.where(content:).first || create_content_tag_for!(content, defaults)
    end
  end

  def create_content_tag_for!(content, defaults = {})
    return if content.is_a?(Assignment) && Assignment::SUBMITTABLE_TYPES.include?(content.submission_types)

    self.class.unique_constraint_retry do |retry_count|
      tag = nil
      tag = content_tags.where(content:).first if retry_count > 0
      tag ||= content_tags.create!(defaults.merge(content:))
      tag
    end
  end

  def cached_content_tag_for(content)
    raise "must call `load_tags!` first" unless @content_tag_index

    if content.is_a?(Assignment) && (submittable = content.submittable_object)
      content = submittable # use one child tag
    end
    @content_tag_index.dig(content.class.base_class.name, content.id)
  end
end

View on GitHub (pinned to 1c9f0bb801)