{"record":{"id":"18ef58846af8c5bc","repo":"instructure/canvas-lms","slug":"course-pace-is-deleted","errorCode":null,"errorMessage":"Course pace is deleted","messagePattern":"Course pace is deleted","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/models/course_pace.rb","lineNumber":157,"sourceCode":"    progress = Progress.create!(context: self, tag: \"course_pace_publish\")\n    run_publish_progress(progress, run_at:, enrollment_ids:)\n    progress\n  end\n\n  def run_publish_progress(progress, run_at: Time.zone.now, enrollment_ids: nil)\n    progress.process_job(self,\n                         :publish,\n                         {\n                           run_at:,\n                           singleton: \"course_pace_publish:#{global_id}\",\n                           on_conflict: :overwrite\n                         },\n                         enrollment_ids:)\n    progress\n  end\n\n  def publish(progress = nil, enrollment_ids: nil)\n    raise \"Course pace is deleted\" if deleted?\n\n    enrollments = enrollment_ids ? course.student_enrollments.where(id: enrollment_ids) : student_enrollments\n    Time.use_zone(course.time_zone) do\n      assignments_to_refresh = Set.new\n      Assignment.suspend_due_date_caching do\n        Assignment.suspend_grading_period_grade_recalculation do\n          progress&.calculate_completion!(0, enrollments.size)\n          enrollments.each do |enrollment|\n            course_pace_module_items.each(&:restore_attributes)\n            # Compressor handles sorting internally\n            compressed_module_items = compress_dates(start_date: nil, enrollment:)\n            dates =\n              CoursePaceDueDatesCalculator.new(self).get_due_dates(compressed_module_items, enrollment)\n            course_pace_module_items.each do |course_pace_module_item|\n              content_tag = course_pace_module_item.module_item\n              assignment = content_tag.assignment\n              next unless assignment\n              next if assignment.only_visible_to_overrides","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/course_pace.rb#L139-L175","documentation":"CoursePace#publish refuses to publish a course pace whose workflow_state is 'deleted'. Publishing recomputes assignment due dates for student enrollments, which makes no sense for a deleted pace, so it raises immediately.","triggerScenarios":"Calling course_pace.publish (directly or from batch/progress jobs) on a pace record with deleted? true; enqueuing publish jobs for paces that were deleted before the job ran.","commonSituations":"A user deletes a pace while a background publish job is queued; batch automation iterates course_paces without filtering out deleted ones; a race between deletion and publish endpoints.","solutions":["Check course_pace.deleted? before calling publish and skip deleted paces.","Reload the record before publishing and handle deletion that happened after enqueueing.","Recreate/un-delete the pace if it should be published (restore workflow_state)."],"exampleFix":"// before\npace.publish(progress)\n\n// after\npace.reload\npace.publish(progress) unless pace.deleted?","handlingStrategy":"validation","validationCode":"raise 'pace deleted' if course_pace.reload.deleted?","typeGuard":"publishable = ->(p) { p.reload.persisted? && !p.deleted? }","tryCatchPattern":"// rescue\nbegin\n  pace.publish(progress)\nrescue RuntimeError => e\n  raise unless e.message == 'Course pace is deleted'\n  Rails.logger.info(\"skipping deleted pace #{pace.id}\")\nend","preventionTips":["Reload paces inside background jobs before publishing","Filter deleted paces from batch publish queries","Handle deletion races with singleton delayed jobs"],"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"}