{"record":{"id":"039bdb5451b860a1","repo":"instructure/canvas-lms","slug":"cannot-call-with-more-than-one-user","errorCode":null,"errorMessage":"Cannot call with more than one user","messagePattern":"Cannot call with more than one user","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"app/models/enrollment.rb","lineNumber":1136,"sourceCode":"  # * An assignment is deleted/undeleted\n  #\n  # * An enrollment is accepted (to address the scenario where a student\n  #   is transferred from one section to another, and final grades need\n  #   to be transferred)\n  #\n  # If some new feature comes up that affects calculation of a user's score,\n  # please add appropriate calls to this so that the cached values don't get\n  # stale! And once you've added the call, add the condition to the comment\n  # here for future enlightenment.\n\n  def self.recompute_final_score(*, **)\n    GradeCalculator.recompute_final_score(*, **)\n  end\n\n  # This method is intended to not duplicate work for a single user.\n  def self.recompute_final_score_in_singleton(user_id, course_id, **opts)\n    # Guard against getting more than one user_id\n    raise ArgumentError, \"Cannot call with more than one user\" if Array(user_id).size > 1\n\n    delay_if_production(singleton: \"Enrollment.recompute_final_score:#{user_id}:#{course_id}:#{opts[:grading_period_id]}\",\n                        max_attempts: 10)\n      .recompute_final_score(user_id, course_id, **opts)\n  end\n\n  def self.recompute_due_dates_and_scores(user_id)\n    Course.where(id: StudentEnrollment.where(user_id:).distinct.select(:course_id)).each do |course|\n      SubmissionLifecycleManager.recompute_users_for_course([user_id], course, nil, update_grades: true)\n    end\n  end\n\n  def self.recompute_final_scores(user_id)\n    StudentEnrollment.where(user_id:).distinct.pluck(:course_id).each do |course_id|\n      recompute_final_score_in_singleton(user_id, course_id)\n    end\n  end\n","sourceCodeStart":1118,"sourceCodeEnd":1154,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/enrollment.rb#L1118-L1154","documentation":"Enrollment.recompute_final_score_in_singleton schedules a singleton delayed job to recompute a final score for exactly one user in one course. The singleton key embeds user_id, so multiple users would collide; it raises ArgumentError if Array(user_id).size > 1.","triggerScenarios":"Calling Enrollment.recompute_final_score_in_singleton([1,2,3], course_id) or passing a collection/relation of users instead of a single id.","commonSituations":"Batch recomputation after grading changes: developer passes an array of student ids to the singleton variant instead of looping or using the plural method.","solutions":["Call the method once per user_id in a loop.","Use Enrollment.recompute_final_score (plural) for batches.","Assert Array(user_id).size == 1 at the call site before invoking."],"exampleFix":"// before\nEnrollment.recompute_final_score_in_singleton(user_ids, course_id)\n// after\nuser_ids.each { |uid| Enrollment.recompute_final_score_in_singleton(uid, course_id) }","handlingStrategy":"validation","validationCode":"raise \"expected a single user id\" unless Array(user_id).size == 1","typeGuard":null,"tryCatchPattern":"begin\n  Enrollment.recompute_final_score_in_singleton(user_id, course_id)\nrescue ArgumentError\n  user_ids.each { |uid| Enrollment.recompute_final_score_in_singleton(uid, course_id) }\nend","preventionTips":["Never pass arrays to *_in_singleton methods","Loop over ids at the call site","Keep singleton call sites single-purpose"],"tags":["activerecord","delayed-job","guard-clause"],"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"}