instructure/canvas-lms · error · RruleValidationError

Missing COUNT or UNTIL

Error message

Missing COUNT or UNTIL

What it means

RruleHelper validates recurring-event RRULE options before converting them to natural language. Canvas does not support never-ending event series because every event in the series must be materialized in the database to support the paginated calendar_events API, so an RRULE must terminate via COUNT or UNTIL. If the rropts hash contains neither key, rrule_validate_common_opts raises RruleValidationError with this message.

Solutions

  1. Add a COUNT (number of occurrences) or UNTIL (end date) clause to the RRULE so the series terminates
  2. If the series is meant to be infinite, split it into bounded segments (e.g. COUNT=365 chunks) since Canvas caps series via RruleHelper::RECURRING_EVENT_LIMIT
  3. Validate the RRULE before saving the event and return a 400 to the client explaining that recurring events must have an end

Example fix

// before
"FREQ=WEEKLY;INTERVAL=1;BYDAY=MO"
// after
"FREQ=WEEKLY;INTERVAL=1;BYDAY=MO;COUNT=10"
Defensive patterns

Strategy: validation

Validate before calling

def valid_rrule_termination?(rropts)
  rropts.key?("COUNT") || rropts.key?("UNTIL")
end
raise ArgumentError, "RRULE needs COUNT or UNTIL" unless valid_rrule_termination?(rropts)

Try / catch

begin
  RruleHelper.rrule_to_natural_language(rropts)
rescue RruleValidationError => e
  render json: { errors: [e.message] }, status: :bad_request
end

Prevention

When it happens

Trigger: Calling RruleHelper.rrule_to_natural_language with an rropts hash that has FREQ (and valid INTERVAL) but omits both the "COUNT" and "UNTIL" keys — e.g. an RRULE string like FREQ=DAILY;INTERVAL=1 with no termination clause.

Common situations: Importing or parsing external calendar data (ICS files) whose RRULEs omit COUNT/UNTIL because they were designed as infinite series; clients constructing recurring calendar events via the API and forgetting the end condition; upstream calendar libraries that allow open-ended recurrence.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15). Data as JSON: /api/errors/858ea36c896a22cf. Report an issue: GitHub.

Appendix: source

Thrown at app/helpers/rrule_helper.rb:61

    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")

    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"),

View on GitHub (pinned to 1c9f0bb801)