instructure/canvas-lms · error · GraphQL::ExecutionError

Invalid section ids

Error message

Invalid section ids

What it means

Raised in the PostAssignmentGradesForSections GraphQL mutation when the set of section records resolved from the caller-supplied section_ids does not match the input list. Either no sections were found, or some ids were invalid/duplicated/inaccessible to the course, so the count of loaded sections differs from input[:section_ids].size. Canvas aborts the post rather than posting to a partial set of sections.

Solutions

  1. Verify every id in section_ids belongs to the assignment's course: course.sections.active.pluck(:id).include?(id)
  2. Remove empty or duplicate entries from section_ids before calling the mutation
  3. Ensure the assignment's grades are published and it is not anonymous (those raise sibling errors before this one)
  4. Fetch current section ids from the course GraphQL query instead of cached client state

Example fix

// before
postAssignmentGradesForSections(assignmentId, sectionIds: staleIds)
// after
const ids = [...new Set(sections.filter(s => s.courseId === course.id).map(s => s.id))]
if (ids.length) postAssignmentGradesForSections(assignmentId, sectionIds: ids)
Defensive patterns

Strategy: validation

Validate before calling

const ids = [...new Set(sectionIds || [])]
const valid = ids.every(id => course.sections.active.some(s => s.id === id || s.legacyNodeId === id))
if (!ids.length || !valid) throw new Error('Invalid section ids before mutation call')

Type guard

function hasValidSectionIds(sections, ids) {
  return Array.isArray(ids) && ids.length > 0 && new Set(ids).size === ids.length &&
    ids.every(id => sections.some(s => s.id === id))
}

Try / catch

try {
  await postAssignmentGradesForSections({ id, sectionIds })
} catch (e) {
  if (e.message.includes('Invalid section ids')) {
    refetchSections(); notify('One or more sections are no longer available')
  } else throw e
}

Prevention

When it happens

Trigger: Calling mutation postAssignmentGradesForSections with section_ids that are empty, contain ids not belonging to the assignment's course, contain duplicates that dedupe on load, reference soft-deleted/inactive sections, or use a malformed id that resolves to no Section.

Common situations: Frontend caches stale section ids after a section is deleted or merged; caller passes GID vs numeric ids inconsistently; posting by section on a course whose sections changed since the UI loaded; passing ids from a different course.

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

Appendix: source

Thrown at app/graphql/mutations/post_assignment_grades_for_sections.rb:49

  def resolve(input:)
    begin
      assignment = AbstractAssignment.find_assignment_or_peer_review(input[:assignment_id])
      course = assignment.context
      sections = course.course_sections.where(id: input[:section_ids])
    rescue ActiveRecord::RecordNotFound
      raise GraphQL::ExecutionError, "not found"
    end

    verify_authorized_action!(assignment, :grade)

    unless assignment.grades_published?
      raise GraphQL::ExecutionError, "Assignments under moderation cannot be posted by section before grades are published"
    end
    raise GraphQL::ExecutionError, "Anonymous assignments cannot be posted by section" if assignment.anonymous_grading?

    if sections.empty? || sections.count != input[:section_ids].size
      raise GraphQL::ExecutionError, "Invalid section ids"
    end

    visible_enrollments = course.apply_enrollment_visibility(course.student_enrollments, current_user, sections)

    submissions_scope = input[:graded_only] ? assignment.submissions.postable : assignment.submissions
    submissions_scope = submissions_scope.joins(user: :enrollments).merge(visible_enrollments)
    progress = course.progresses.new(tag: "post_assignment_grades_for_sections")

    posting_params = {
      graded_only: !!input[:graded_only],
      section_names: sections&.pluck(:name)
    }

    if progress.save
      progress.process_job(
        assignment,
        :post_submissions,
        { preserve_method_args: true, priority: Delayed::HIGH_PRIORITY },

View on GitHub (pinned to 1c9f0bb801)