instructure/canvas-lms · error · ActiveRecord::Rollback
Failed updating an event in the series, update not saved
Error message
Failed updating an event in the series, update not saved
What it means
In CalendarEventsApiController#destroy_from_series, when deleting/updating one event in a recurring (rrule) series, each front-half sibling event must both be permitted (grants_any_right? :update) and save successfully with the new rrule params. Any failure sets an error hash with message 'Failed updating an event in the series, update not saved' (401 for permission, generic otherwise) and raises ActiveRecord::Rollback, aborting the whole transaction so no partial update persists.
Solutions
- Ensure the acting user has :update rights on every event in the series (manage all calendars involved), or delete only occurrences they own
- Inspect the failing event's validation errors (event.errors) and fix the underlying validation problem
- Retry after concurrent-modification conflicts, re-reading the series events
- As an admin, operate as the series owner or use site-admin privileges
Example fix
// before
event.update(params_for_update_front_half) # fails validation, silent rollback
// after
unless event.update(params_for_update_front_half)
Rails.logger.warn("series event #{event.id}: #{event.errors.full_messages}")
error = { message: event.errors.full_messages.to_sentence }
raise ActiveRecord::Rollback
end Defensive patterns
Strategy: try-catch
Validate before calling
// Ruby pre-check before series operations
unless series_events.all? { |e| e.grants_any_right?(user, session, :update) }
raise PermissionDenied, 'user cannot update all events in the series'
end Try / catch
begin destroy_from_series(event) rescue ActiveRecord::Rollback # whole series update aborted; inspect error hash / event.errors, reload, then retry end
Prevention
- Confirm update rights on every event in the series before bulk edits
- Operate on series as the owner or with admin privileges
- Reload the series before retrying after concurrent-modification failures
When it happens
Trigger: DELETE/update on a series event where at least one sibling event in the series is owned by/visible to a user lacking :update permission, or an event.update fails validation (e.g. invalid rrule count/until params, locked events, or concurrent modification).
Common situations: A user deletes their own occurrence of a shared series they don't fully manage; series containing events across contexts with differing permissions; validation failures from recomputed rrule count/until; stale events deleted concurrently by another user.
Related errors
- ineligible participant
- invalid participant
- Cannot change locked status on granular permission
- Must be a siteadmin user!
- not an appointment
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/bc9699e0586c5506.
Report an issue: GitHub.
Appendix: source
Thrown at app/controllers/calendar_events_api_controller.rb:910
if event.appointment_group && @event.appointment_group.appointments.count == 0
event.appointment_group.destroy(@current_user)
end
else
error = event.errors
raise ActiveRecord::Rollback
end
end
if params[:which] == "following"
# the remaining series just got shorter. reflect that in the rrrule
front_half_events = (find_which_series_events(target_event: @event, which: "all", for_update: false) - events).to_a
unless front_half_events.empty?
params_for_update_front_half = ActionController::Parameters.new(rrule: update_rrule_count_or_until(@event[:rrule], front_half_events.length)).permit(:rrule)
front_half_events.each do |event|
event.updating_user = @current_user
unless event.grants_any_right?(@current_user, session, :update)
error = { message: t("Failed updating an event in the series, update not saved"), status: :unauthorized }
raise ActiveRecord::Rollback
end
unless event.update(params_for_update_front_half)
error = { message: t("Failed updating an event in the series, update not saved") }
raise ActiveRecord::Rollback
end
end
end
end
end
end
return render json: error, status: :bad_request if error
@event.context.touch # assume all events in the series belong to the same context
json = (events + front_half_events).map do |event|
event.reloadView on GitHub (pinned to 1c9f0bb801)