instructure/canvas-lms · error · ActiveRecord::RecordNotSaved
Cannot archive a deleted LearningOutcomeGroup
Error message
Cannot archive a deleted LearningOutcomeGroup
What it means
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'.
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.
Example fix
// before LearningOutcomeGroup.where(context: course).each(&:archive!) // after LearningOutcomeGroup.where(context: course, workflow_state: "active").each(&:archive!)
Defensive patterns
Strategy: try-catch
Validate before calling
groups = groups.where(workflow_state: "active") groups.each(&:archive!)
Try / catch
begin
group.archive!
rescue ActiveRecord::RecordNotSaved
Rails.logger.info("group #{group.id} deleted; skip archive")
end Prevention
- Scope bulk archiving to workflow_state: 'active'
- Reload before archiving stale records
- Continue past RecordNotSaved in bulk loops
When it happens
Trigger: Calling group.archive! on a LearningOutcomeGroup with workflow_state 'deleted', e.g. from a stale UI list or concurrent deletion while archiving.
Common situations: Outcome group deleted while another tab or job archives it; bulk archival scripts iterating a stale relation including deleted groups.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Cannot archive a deleted LearningOutcome
- Cannot unarchive a deleted LearningOutcome
- Cannot unarchive a deleted LearningOutcomeGroup
- A course did not pass validation
- A # user did not pass validation (user: # , # : # , error…
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/e161c747b2cbc147.
Report an issue: GitHub.
Appendix: source
Thrown at app/models/learning_outcome_group.rb:279
end
child_outcome_groups.active.each do |outcome_group|
outcome_group.skip_tag_touch = true if @skip_tag_touch
outcome_group.destroy
end
self.workflow_state = "deleted"
save!
end
end
def archive!
# Only active groups can be archived
if workflow_state == "active"
self.workflow_state = "archived"
self.archived_at = Time.now.utc
save!
elsif workflow_state == "deleted"
raise ActiveRecord::RecordNotSaved, "Cannot archive a deleted LearningOutcomeGroup"
end
end
def unarchive!
# Only archived groups can be unarchived
if workflow_state == "archived"
self.workflow_state = "active"
self.archived_at = nil
save!
elsif workflow_state == "deleted"
raise ActiveRecord::RecordNotSaved, "Cannot unarchive a deleted LearningOutcomeGroup"
end
end
scope :active, -> { where("learning_outcome_groups.workflow_state NOT IN ('deleted', 'archived')") }
scope :active_first, -> { order(Arel.sql("CASE WHEN workflow_state = 'active' THEN 0 ELSE 1 END")) }
scope :archived, -> { where("learning_outcome_groups.workflow_state = 'archived' AND learning_outcome_groups.archived_at IS NOT NULL") }
View on GitHub (pinned to 1c9f0bb801)