instructure/canvas-lms · error · RuntimeError
set the collection owner first
Error message
set the collection owner first
What it means
MasterCourses::CollectionRestrictor extends the blueprint course restrictor to collections whose owner is a separate model (e.g. AssessmentQuestion banks). restrict_columns requires that the collection owner association was declared beforehand via set_collection_owner_association; without it the pseudocolumn changes cannot be forwarded to the owner class, so it raises immediately.
Solutions
- Call set_collection_owner_association (with the owner association symbol) before any restrict_columns call in the model
- Ensure the included-hook order puts the association declaration above restrict_columns calls
- Compare with an existing restrictor model (e.g. AssessmentQuestion) to copy the correct setup
Example fix
# before included do restrict_columns(:content, [...]) end # after included do set_collection_owner_association :assessment_question restrict_columns(:content, [...]) end
Defensive patterns
Strategy: validation
Validate before calling
raise 'owner not set' unless Model.reflections.key?(Model.collection_owner_association.to_s) # run in an initializer/spec check before restrict_columns
Type guard
def collection_restrictor_configured?(klass) klass.respond_to?(:collection_owner_association) && !!klass.collection_owner_association end
Prevention
- Always call set_collection_owner_association as the first statement in the included block
- Add a spec asserting the model's restrictor setup loads
When it happens
Trigger: Calling klass.restrict_columns(edit_type, columns) on a model that includes MasterCourses::CollectionRestrictor before calling set_collection_owner_association (e.g. in a model's included block ordering issue).
Common situations: Adding the restrictor to a new model and forgetting the owner association declaration; refactoring model concerns so restrict_columns runs before the association is set; copy-pasting restrictor setup from another model without its owner declaration.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- cannot change column: captions - locked by Master Course
- invalid edit type
- invalid restriction type
- must call `load_tags!` first
- A new_id, '# ', referenced an existing # and the # with #…
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/880c94814f7f45e4.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/master_courses/collection_restrictor.rb:39
# this is super similar to the normal Restrictor - the main difference is that these models are locked by some parent object
# e.g. this is a quiz question locked by a quiz
# even though they do a lot of the same things, i couldn't figure out a way to keep them together that didn't make things super convoluted
# FYI you'll have to directly hook code that replicates the behavior of
# `check_before_overwriting_child_content_on_import` into the respective importers if you add this to new objects
def self.included(klass)
klass.include MasterCourses::Restrictor::CommonMethods
klass.extend ClassMethods
klass.cattr_accessor :collection_owner_association # this is the association to find the quiz
klass.after_update :mark_downstream_changes, if: -> { klass != Lti::LineItem || Account.site_admin.feature_enabled?(:blueprint_line_item_support) }
end
module ClassMethods
def restrict_columns(edit_type, columns)
raise "set the collection owner first" unless collection_owner_association
super
owner_class = reflections[collection_owner_association.to_s].klass
owner_class.restrict_columns(edit_type, pseudocolumn_for_type(edit_type)) # e.g. add "assessment_questions_content" as a restricted column
end
def pseudocolumn_for_type(type) # prepend with table name because it looks better than "Quizzes::QuizQuestion"
"#{table_name}_#{type}"
end
end
def check_restrictions?
true
end
def owner_for_restrictions
send(self.class.base_class.collection_owner_association)
endView on GitHub (pinned to 1c9f0bb801)