instructure/canvas-lms · warning · RruleValidationError
Invalid FREQ
Error message
Invalid FREQ '%{freq}' What it means
rrule_to_natural_language maps an RRULE's FREQ (DAILY/WEEKLY/MONTHLY/YEARLY). An unrecognized FREQ raises RruleValidationError "Invalid FREQ '%{freq}'"; the surrounding rescue logs it and returns nil, so calendar events silently lose their natural-language description.
Solutions
- Reject or normalize sub-daily FREQ values at ICS import time.
- Catch RruleValidationError upstream and render a generic 'repeats on a custom schedule' string instead of nil.
- Fix the stored rrule to a supported FREQ (DAILY/WEEKLY/MONTHLY/YEARLY).
- Add validation on CalendarEvent save to whitelist supported FREQ values.
Example fix
// before freq = rropts['FREQ'] # 'HOURLY' // after unless %w[DAILY WEEKLY MONTHLY YEARLY].include?(rropts['FREQ']) rropts['FREQ'] = 'DAILY' # or reject the event at import end
Defensive patterns
Strategy: validation
Validate before calling
return nil unless %w[DAILY WEEKLY MONTHLY YEARLY].include?(rropts['FREQ'])
Try / catch
desc = begin
rrule_to_natural_language(rrule)
rescue RruleValidationError
I18n.t('repeats on a custom schedule')
end Prevention
- Whitelist supported FREQ values at ICS import
- Reject sub-daily recurrences (SECONDLY/MINUTELY/HOURLY) explicitly
- Remember rrule_to_natural_language rescues and returns nil — check the return value
When it happens
Trigger: Rendering a calendar_event_json for an event whose rrule field has FREQ values like SECONDLY, MINUTELY, HOURLY, or a malformed/empty FREQ (e.g. from an imported .ics with sub-daily recurrence).
Common situations: Importing external ICS feeds using SECONDLY/HOURLY recurrences; hand-edited rrule strings; data migrations from other calendar systems.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- Missing COUNT or UNTIL
- A yearly RRULE must include BYDAY or BYMONTHDAY
- COUNT must be > 0
- COUNT must be <=
- INTERVAL must be > 0
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/75c94c904614a531.
Report an issue: GitHub.
Appendix: source
Thrown at app/helpers/rrule_helper.rb:44
# rubocop:disable Style/IfInsideElse
module RruleHelper
RECURRING_EVENT_LIMIT = 400
def rrule_to_natural_language(rrule)
rropts = rrule_parse(rrule)
rrule_validate_common_opts(rropts)
case rropts["FREQ"]
when "DAILY"
parse_daily(rropts)
when "WEEKLY"
parse_weekly(rropts)
when "MONTHLY"
parse_monthly(rropts)
when "YEARLY"
parse_yearly(rropts)
else
raise RruleValidationError, I18n.t("Invalid FREQ '%{freq}'", freq: rropts["FREQ"])
end
rescue => e
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")
View on GitHub (pinned to 1c9f0bb801)