{"record":{"id":"81cc7db3d7792b25","repo":"instructure/canvas-lms","slug":"course-pace-is-not-deleted","errorCode":null,"errorMessage":"Course pace is not deleted","messagePattern":"Course pace is not deleted","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/models/course_pace.rb","lineNumber":245,"sourceCode":"\n      # Clear caches\n      Assignment.clear_cache_keys(assignments_to_refresh, :availability)\n      SubmissionLifecycleManager.recompute_course(course, assignments: assignments_to_refresh, update_grades: true)\n\n      # Maintain the weights of the module items\n      course_pace_module_items.each(&:restore_attributes)\n\n      # Explicitly complete the progress so we aren't left with any hanging progresses\n      progress.complete! if progress&.running?\n\n      # Mark as published\n      log_module_items_count\n      update(workflow_state: \"active\", published_at: Time.zone.now)\n    end\n  end\n\n  def republish_paces_for_affected_enrollments\n    raise \"Course pace is not deleted\" unless deleted?\n\n    grouped_paces_and_enrollments = student_enrollments.group_by do |enrollment|\n      student_section_ids = enrollment.user.student_enrollments.shard(shard).where(course:).active.pluck(:course_section_id)\n      pace = course.course_paces.published.where(course_section_id: student_section_ids).last\n      pace || course.course_paces.published.primary.take\n    end\n    grouped_paces_and_enrollments.each do |pace, enrollments|\n      pace.create_publish_progress(enrollment_ids: enrollments.pluck(:id))\n    end\n  end\n\n  def published?\n    active?\n  end\n\n  def primary?\n    !deleted? && course_section_id.nil? && user_id.nil?\n  end","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/course_pace.rb#L227-L263","documentation":"CoursePace#republish_paces_for_affected_enrollments reassigns enrollments to remaining published paces after a pace is deleted; it is only valid on a deleted pace, so it raises when called on a pace that is not deleted.","triggerScenarios":"Calling course_pace.republish_paces_for_affected_enrollments on an active or unpublished pace; invoking it manually (or via job) instead of through the deletion workflow.","commonSituations":"Retrying a cleanup job whose target pace was already processed/restored; calling the method out of order in custom deletion flows; re-running jobs after a pace was un-deleted.","solutions":["Only call this after successfully deleting the pace (guard with pace.deleted?).","Ensure the deletion transaction committed before enqueueing the republish job.","If the pace was restored, republish via publish instead of this cleanup method."],"exampleFix":"// before\npace.republish_paces_for_affected_enrollments\n\n// after\npace.destroy_or_delete_with_enrollment_republish # or:\npace.reload\npace.republish_paces_for_affected_enrollments if pace.deleted?","handlingStrategy":"validation","validationCode":"raise 'pace not deleted' unless course_pace.reload.deleted?","typeGuard":"republishable = ->(p) { p.reload.deleted? }","tryCatchPattern":null,"preventionTips":["Call this method only from the deletion workflow","Verify the delete transaction committed before enqueueing republish","Skip/complete idempotently when jobs are retried after restore"],"tags":["ruby-on-rails","course-paces","state-machine"],"backgroundTag":"invalid-state-transition","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"}