{"record":{"id":"a29c70967e2a71f7","repo":"instructure/canvas-lms","slug":"cannot-unarchive-a-deleted-learningoutcome","errorCode":null,"errorMessage":"Cannot unarchive a deleted LearningOutcome","messagePattern":"Cannot unarchive a deleted LearningOutcome","errorType":"validation","errorClass":"ActiveRecord::RecordNotSaved","httpStatus":null,"severity":"error","filePath":"app/models/learning_outcome.rb","lineNumber":423,"sourceCode":"  def archive!\n    # Only active outcomes 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 LearningOutcome\"\n    end\n  end\n\n  def unarchive!\n    # Only archived outcomes 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 LearningOutcome\"\n    end\n  end\n\n  def assessed?(course = nil)\n    if course\n      learning_outcome_results.active.where(context_id: course, context_type: \"Course\").exists?\n    elsif learning_outcome_results.active.loaded?\n      learning_outcome_results.active.any?\n    else\n      learning_outcome_results.active.exists?\n    end\n  end\n\n  def tie_to(context)\n    @tied_context = context\n  end\n\n  def mastery_points","sourceCodeStart":405,"sourceCodeEnd":441,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/learning_outcome.rb#L405-L441","documentation":"LearningOutcome#unarchive! returns an archived outcome to 'active' and clears archived_at. Unarchiving a deleted outcome is an invalid transition, raising ActiveRecord::RecordNotSaved when workflow_state is 'deleted'.","triggerScenarios":"Calling outcome.unarchive! on a deleted LearningOutcome — e.g. a stale UI tab or a job operating on a record deleted after load.","commonSituations":"Outcome deleted while archived; user clicks restore from a stale list; importers toggling states without reloading.","solutions":["Guard: only call unarchive! when workflow_state == 'archived'.","Reload the outcome and re-check state before the transition.","Rescue ActiveRecord::RecordNotSaved and surface 'outcome was deleted' to the user.","Restore deleted outcomes via the proper deletion-undo path, not unarchive!."],"exampleFix":"// before\noutcome.unarchive!\n// after\noutcome.reload.unarchive! if outcome.reload.workflow_state == \"archived\"","handlingStrategy":"try-catch","validationCode":"outcome.reload.unarchive! if outcome.reload.workflow_state == \"archived\"","typeGuard":null,"tryCatchPattern":"begin\n  outcome.unarchive!\nrescue ActiveRecord::RecordNotSaved\n  flash[:error] = \"This outcome was deleted and cannot be unarchived\"\nend","preventionTips":["Check workflow_state before unarchiving","Reload records before transitions after async deletes","Restore deleted outcomes via the deletion-undo path, not unarchive!"],"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"}