instructure/canvas-lms · error · ArgumentError
Cannot call with more than 1000 enrollments
Error message
Cannot call with more than 1000 enrollments
What it means
Enrollment::BatchStateUpdater.destroy_batch mass-deletes enrollments in one transaction, preloading users, observers and root accounts to fix up counters. The work is capped at 1000 enrollments to keep transactions and job payloads bounded; larger batches raise ArgumentError before any deletion.
Solutions
- Partition the batch into chunks of <= 1000 and call destroy_batch per chunk.
- Ensure batch is a proper relation/id scope so count is cheap.
- For very large deletes, use a dedicated workflow (e.g. course conclude/delete) instead of one batch call.
Example fix
// before
Enrollment::BatchStateUpdater.destroy_batch(enrollments)
// after
enrollments.find_in_batches(batch_size: 1000) { |chunk| Enrollment::BatchStateUpdater.destroy_batch(chunk) } Defensive patterns
Strategy: validation
Validate before calling
enrollments.find_in_batches(batch_size: 1000) { |chunk| Enrollment::BatchStateUpdater.destroy_batch(chunk) } Try / catch
begin
Enrollment::BatchStateUpdater.destroy_batch(batch)
rescue ArgumentError
batch.find_in_batches(batch_size: 1000) { |c| Enrollment::BatchStateUpdater.destroy_batch(c) }
end Prevention
- Always chunk large enrollment sets to <= 1000
- Check batch.count before bulk calls
- Prefer id-based relations over loaded arrays for counting
When it happens
Trigger: Calling destroy_batch(batch) where batch.count > 1000, e.g. deleting all enrollments of a large course or a big SIS batch-mode delete.
Common situations: Course merges/unpublishes, SIS imports deleting large enrollment sets, cleanup rake tasks passing unscoped relations.
Related errors
- A maximum of 50 assessees can be provided at once
- A maximum of 50 assessors can be provided at once
- argument is not an Account
- Cannot call with more than one user
- COUNT must be <=
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/e61ef00be39f71bc.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/enrollment/batch_state_updater.rb:49
# after_save :copy_scores_from_existing_enrollment, if: :need_to_copy_scores? - not needed for destroy
# after_save :restore_submissions_and_scores - not needed for destroy
# after_save :clear_email_caches - handled in clear_email_caches
# after_save :cancel_future_appointments - handled in cancel_future_appointments
# after_save :update_linked_enrollments - handled in update_linked_enrollments
# after_save :set_update_cached_due_dates - handled in update_cached_due_dates
# after_save :touch_graders_if_needed - handled in touch_all_graders_if_needed
# after_save :reset_notifications_cache - handled in reset_notifications_cache
# after_save :dispatch_invitations_later - not needed for destroy
# after_save :add_to_favorites_later - not needed for destroy
# after_commit :update_cached_due_dates - handled in update_cached_due_dates
# after_save :update_assignment_overrides_if_needed - handled in update_assignment_overrides
# after_save :needs_grading_count_updated -handled in needs_grading_count_updated
# after_commit :sync_microsoft_group - handled in sync_microsoft_group
#
# ignore_due_date_caching_for: optional hash of (course_id => user_ids_array) of users to _not_
# re-cache their scores for (presumably the caller already queued one of their own)
def self.destroy_batch(batch, sis_batch: nil, batch_mode: false, ignore_due_date_caching_for: {})
raise ArgumentError, "Cannot call with more than 1000 enrollments" if batch.count > 1000
Enrollment.transaction do
# cache some data before the destroy that is needed after the destroy
@invited_user_ids = Enrollment.where(id: batch, workflow_state: "invited").distinct.pluck(:user_id)
@students = Enrollment.of_student_type.where(id: batch).preload({ user: :linked_observers }, :root_account).to_a
@students.each do |e|
e.workflow_state = "deleted"
e.readonly!
end
@user_course_tuples = Enrollment.where(id: batch).active.select(%i[user_id course_id]).distinct.to_a
@user_ids = Enrollment.where(id: batch).order(:user_id).distinct.pluck(:user_id)
@courses = Course.where(id: Enrollment.where(id: batch).select(:course_id).distinct).to_a
@root_account = @courses.first.root_account
@data = mark_enrollments_as_deleted(batch, sis_batch:, batch_mode:)
gms = remove_group_memberships(batch, @courses, @user_ids, sis_batch:, batch_mode:)
@data&.push(*gms) if gms
# touch users after removing group_memberships to invalidate the cache.
cancel_future_appointments(@courses, @user_ids)View on GitHub (pinned to 1c9f0bb801)