{"record":{"id":"6281813e64eb3eeb","repo":"instructure/canvas-lms","slug":"invalid-consumer-consumer-class","errorCode":null,"errorMessage":"Invalid consumer #{consumer.class}","messagePattern":"Invalid consumer #(.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/models/discussion_topic/scoped_to_sections.rb","lineNumber":30,"sourceCode":"# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY\n# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\n# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more\n# details.\n#\n# You should have received a copy of the GNU Affero General Public License along\n# with this program. If not, see <http://www.gnu.org/licenses/>.\n\nclass DiscussionTopic::ScopedToSections < ScopeFilter\n  # tl;dr this pattern isn't intended to be re-used\n  #\n  # DiscussionTopic::ScopedToUser is currently used in tandem with this class. The\n  # functionality of this class _should_ belong in there. However, because ScopedToUser\n  # is used by multiple classes for multiple contexts, we are opting to separate the\n  # logic to this one class. This allows for a fix for filtering visible discussions\n  # prior to pagination, whereas previously we were paginating prior to filtering.\n  # That allowed for some pages to end up blank. See https://instructure.atlassian.net/browse/KNO-372\n  def self.for(consumer, context, user, relation)\n    raise \"Invalid consumer #{consumer.class}\" unless consumer.instance_of?(DiscussionTopicsController)\n\n    DiscussionTopic::ScopedToSections.new(context, user, relation)\n  end\n\n  def scope\n    concat_scope do\n      scope_for_user_sections(@relation)\n    end\n  end\n\n  private\n\n  def scope_for_user_sections(scope)\n    return scope if context.grants_any_right?(\n      user,\n      :read_as_admin,\n      :manage_grades,\n      *RoleOverride::GRANULAR_MANAGE_ASSIGNMENT_PERMISSIONS,","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/discussion_topic/scoped_to_sections.rb#L12-L48","documentation":"DiscussionTopic::ScopedToSections.for is a factory that only accepts a DiscussionTopicsController instance as the consumer. Any other controller or object passed in (e.g. another controller reusing ScopedToUser-style scoping) is rejected with this error, since the section-scoped filtering logic is written specifically for discussion topic views.","triggerScenarios":"Calling DiscussionTopic::ScopedToSections.for(obj, context, user, relation) where obj.class is not DiscussionTopicsController — e.g. passing the controller class instead of an instance, self from a different controller, or nil.","commonSituations":"Refactoring a controller to reuse section scoping and passing the wrong object; calling .for from an API controller (DiscussionTopics::ApiController) that this class intentionally does not support; tests passing a double/stub that is not an instance.","solutions":["Only call .for from DiscussionTopicsController with self as the consumer","If you need scoping elsewhere, build DiscussionTopic::ScopedToSections.new(context, user, relation) directly or extract shared logic","Pass the controller instance, not the class or a different controller","Use DiscussionTopic::ScopedToUser for non-controller contexts as the older classes do"],"exampleFix":"// before\ndiscussions = DiscussionTopic::ScopedToSections.for(DiscussionTopicsController, @context, @current_user, scope)\n// after\ndiscussions = DiscussionTopic::ScopedToSections.for(self, @context, @current_user, scope) # inside DiscussionTopicsController","handlingStrategy":"type-guard","validationCode":"raise 'wrong consumer' unless consumer.instance_of?(DiscussionTopicsController)\nDiscussionTopic::ScopedToSections.for(consumer, context, user, relation)","typeGuard":"def discussion_topics_controller?(obj)\n  obj.instance_of?(DiscussionTopicsController)\nend","tryCatchPattern":"begin\n  DiscussionTopic::ScopedToSections.for(self, context, user, relation)\nrescue RuntimeError => e\n  raise unless e.message.start_with?('Invalid consumer')\n  relation # fall back to unscoped relation\nend","preventionTips":["Call .for only with self from DiscussionTopicsController","Use ScopedToSections.new directly when no controller is involved","Don't pass controller classes or doubles"],"tags":["ruby","rails","discussion-topics","factory-guard","invalid-argument"],"backgroundTag":"invalid-argument-value","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}