instructure/canvas-lms · error · QueryError

Searching for announcements in sections must span exactly…

Error message

Searching for announcements in sections must span exactly one course

What it means

DiscussionTopic.in_sections is a scope that builds an announcements query for a given array of CourseSection objects. It requires that all provided sections belong to exactly one course; otherwise the query would span multiple courses. When the unique course_id set has a size other than 1 (including zero sections), it raises QueryError.

Solutions

  1. Group sections by course_id and call in_sections once per course, merging results.
  2. Verify before calling: raise or short-circuit if sections.map(&:course_id).uniq.size != 1.
  3. Handle empty input separately instead of passing it to in_sections.

Example fix

// before
Announcement.in_sections(sections)
// after
sections.group_by(&:course_id).each do |_, secs|
  Announcement.in_sections(secs)
end
Defensive patterns

Strategy: validation

Validate before calling

course_ids = sections.map(&:course_id).uniq
raise "sections must span exactly one course" unless course_ids.size == 1

Try / catch

begin
  Announcement.in_sections(sections)
rescue QueryError => e
  Rails.logger.warn("in_sections rejected: #{e.message}")
  sections.group_by(&:course_id).flat_map { |_, s| Announcement.in_sections(s) }
end

Prevention

When it happens

Trigger: Calling DiscussionTopic.in_sections(sections) with sections from two or more different courses, or with an empty/unmatched relation that yields no course_ids.

Common situations: Account-level dashboards aggregating section-scoped announcements across multiple courses; passing a relation filtered by a deleted course (empty set).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at app/models/discussion_topic.rb:906

  class QueryError < StandardError
    attr_accessor :status_code

    def initialize(message = nil, status_code = nil)
      super(message)
      self.status_code = status_code
    end
  end

  # Retrieves all the *course* (as oppposed to group) discussion topics that apply
  # to the given sections.  Group topics will not be returned.  TODO: figure out
  # a good way to deal with group topics here.
  #
  # Takes in an array of section objects, and it is required that they all belong
  # to the same course.  At least one section must be provided.
  scope :in_sections, lambda { |course_sections|
    course_ids = course_sections.pluck(:course_id).uniq
    if course_ids.length != 1
      raise QueryError, I18n.t("Searching for announcements in sections must span exactly one course")
    end

    course_id = course_ids.first
    joins("LEFT OUTER JOIN #{DiscussionTopicSectionVisibility.quoted_table_name}
           AS discussion_section_visibilities ON discussion_topics.is_section_specific = true AND
           discussion_section_visibilities.discussion_topic_id = discussion_topics.id")
      .where("discussion_topics.context_type = 'Course' AND
             discussion_topics.context_id = :course_id",
             { course_id: })
      .where("discussion_section_visibilities.id IS null OR
             (discussion_section_visibilities.workflow_state = 'active' AND
              discussion_section_visibilities.course_section_id IN (:course_sections))",
             { course_sections: course_sections.pluck(:id) }).distinct
  }

  scope :discussion_topic_section_visibility_scope, lambda { |student|
    DiscussionTopicSectionVisibility
      .select(1)

View on GitHub (pinned to 1c9f0bb801)