instructure/canvas-lms · error · RruleValidationError
COUNT must be <=
Error message
COUNT must be <= %{limit} What it means
Canvas materializes every occurrence of a recurring series as database rows, so series length is capped at RruleHelper::RECURRING_EVENT_LIMIT. If rropts["COUNT"].to_i exceeds that limit, rrule_validate_common_opts raises RruleValidationError with the limit interpolated into the message.
Solutions
- Reduce COUNT to at most RruleHelper::RECURRING_EVENT_LIMIT
- Split the series into multiple smaller recurring events (chunks within the limit)
- Check the limit constant and reject/clip the value in the API layer before invoking RruleHelper
Example fix
// before "FREQ=DAILY;INTERVAL=1;COUNT=9999" // after "FREQ=DAILY;INTERVAL=1;COUNT=365" # <= RruleHelper::RECURRING_EVENT_LIMIT
Defensive patterns
Strategy: validation
Validate before calling
limit = RruleHelper::RECURRING_EVENT_LIMIT
count = rropts["COUNT"].to_i
raise ArgumentError, "COUNT exceeds #{limit}" if count > limit Try / catch
begin
RruleHelper.rrule_to_natural_language(rropts)
rescue RruleValidationError => e
render json: { errors: [e.message] }, status: :bad_request
end Prevention
- Clip or reject COUNT at the client with the same limit constant
- Import ICS rules by chunking long series
- Read RruleHelper::RECURRING_EVENT_LIMIT rather than hardcoding a guess
When it happens
Trigger: Calling RruleHelper.rrule_to_natural_language with an rropts hash whose "COUNT" value is a positive integer greater than RruleHelper::RECURRING_EVENT_LIMIT — e.g. COUNT=1000 when the limit is lower.
Common situations: Importing ICS calendars with year-long or infinite daily recurrences encoded as very large COUNTs; clients computing COUNT as days-in-a-range without checking the platform cap; UIs letting users pick arbitrary end counts.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/91958fc02745e69c.
Report an issue: GitHub.
Appendix: source
Thrown at app/helpers/rrule_helper.rb:65
logger.error "RRULE to natural language failure: #{e}"
nil
end
def rrule_parse(rrule)
Hash[*rrule.sub(/^RRULE:/, "").split(/[;=]/)]
end
def rrule_validate_common_opts(rropts)
raise RruleValidationError, I18n.t("Missing INTERVAL") unless rropts.key?("INTERVAL")
raise RruleValidationError, I18n.t("INTERVAL must be > 0") unless rropts["INTERVAL"].to_i > 0
# We do not support never ending series because each event in the series
# must get created in the db to support the paginated calendar_events api
raise RruleValidationError, I18n.t("Missing COUNT or UNTIL") unless rropts.key?("COUNT") || rropts.key?("UNTIL")
if rropts.key?("COUNT")
raise RruleValidationError, I18n.t("COUNT must be > 0") unless rropts["COUNT"].to_i > 0
raise RruleValidationError, I18n.t("COUNT must be <= %{limit}", limit: RruleHelper::RECURRING_EVENT_LIMIT) unless rropts["COUNT"].to_i <= RruleHelper::RECURRING_EVENT_LIMIT
else
begin
format_date(rropts["UNTIL"])
rescue
raise RruleValidationError, I18n.t("Invalid UNTIL '%{until_date}'", until_date: rropts["UNTIL"])
end
end
end
private
DAYS_OF_WEEK = {
"SU" => I18n.t("Sun"),
"MO" => I18n.t("Mon"),
"TU" => I18n.t("Tue"),
"WE" => I18n.t("Wed"),
"TH" => I18n.t("Thu"),
"FR" => I18n.t("Fri"),View on GitHub (pinned to 1c9f0bb801)