{"record":{"id":"e161c747b2cbc147","repo":"instructure/canvas-lms","slug":"cannot-archive-a-deleted-learningoutcomegroup","errorCode":null,"errorMessage":"Cannot archive a deleted LearningOutcomeGroup","messagePattern":"Cannot archive a deleted LearningOutcomeGroup","errorType":"validation","errorClass":"ActiveRecord::RecordNotSaved","httpStatus":null,"severity":"error","filePath":"app/models/learning_outcome_group.rb","lineNumber":279,"sourceCode":"      end\n      child_outcome_groups.active.each do |outcome_group|\n        outcome_group.skip_tag_touch = true if @skip_tag_touch\n        outcome_group.destroy\n      end\n\n      self.workflow_state = \"deleted\"\n      save!\n    end\n  end\n\n  def archive!\n    # Only active groups can be archived\n    if workflow_state == \"active\"\n      self.workflow_state = \"archived\"\n      self.archived_at = Time.now.utc\n      save!\n    elsif workflow_state == \"deleted\"\n      raise ActiveRecord::RecordNotSaved, \"Cannot archive a deleted LearningOutcomeGroup\"\n    end\n  end\n\n  def unarchive!\n    # Only archived groups can be unarchived\n    if workflow_state == \"archived\"\n      self.workflow_state = \"active\"\n      self.archived_at = nil\n      save!\n    elsif workflow_state == \"deleted\"\n      raise ActiveRecord::RecordNotSaved, \"Cannot unarchive a deleted LearningOutcomeGroup\"\n    end\n  end\n\n  scope :active, -> { where(\"learning_outcome_groups.workflow_state NOT IN ('deleted', 'archived')\") }\n  scope :active_first, -> { order(Arel.sql(\"CASE WHEN workflow_state = 'active' THEN 0 ELSE 1 END\")) }\n  scope :archived, -> { where(\"learning_outcome_groups.workflow_state = 'archived' AND learning_outcome_groups.archived_at IS NOT NULL\") }\n","sourceCodeStart":261,"sourceCodeEnd":297,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/learning_outcome_group.rb#L261-L297","documentation":"LearningOutcomeGroup#archive! archives an active outcome group, stamping archived_at. Archiving a deleted group is an invalid state transition, raising ActiveRecord::RecordNotSaved when workflow_state is 'deleted'.","triggerScenarios":"Calling group.archive! on a LearningOutcomeGroup with workflow_state 'deleted', e.g. from a stale UI list or concurrent deletion while archiving.","commonSituations":"Outcome group deleted while another tab or job archives it; bulk archival scripts iterating a stale relation including deleted groups.","solutions":["Filter scope to active groups: where(workflow_state: 'active').each(&:archive!).","Reload the record and verify state == 'active' before archiving.","Rescue ActiveRecord::RecordNotSaved per group in bulk operations so one bad record doesn't abort the loop.","Do nothing if the group is already deleted."],"exampleFix":"// before\nLearningOutcomeGroup.where(context: course).each(&:archive!)\n// after\nLearningOutcomeGroup.where(context: course, workflow_state: \"active\").each(&:archive!)","handlingStrategy":"try-catch","validationCode":"groups = groups.where(workflow_state: \"active\")\ngroups.each(&:archive!)","typeGuard":null,"tryCatchPattern":"begin\n  group.archive!\nrescue ActiveRecord::RecordNotSaved\n  Rails.logger.info(\"group #{group.id} deleted; skip archive\")\nend","preventionTips":["Scope bulk archiving to workflow_state: 'active'","Reload before archiving stale records","Continue past RecordNotSaved in bulk loops"],"tags":["activerecord","state-machine","outcomes"],"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"}