instructure/canvas-lms · error · RruleValidationError
Invalid BYDAY
Error message
Invalid BYDAY '%{byday}' What it means
parse_byday splits a BYDAY value on commas and matches each item against /\A([-+]?\d+)?([A-Z]{2})\z/ — an optional signed occurrence number followed by exactly two uppercase letters for the weekday (MO, TU, WE, TH, FR, SA, SU). Any item failing this regex raises RruleValidationError with "Invalid BYDAY".
Solutions
- Use two-letter uppercase weekday codes: BYDAY=MO,TU,WE,TH,FR,SA,SU
- For nth-weekday recurrence use a signed integer prefix, e.g. BYDAY=2MO or BYDAY=-1FR (last Friday)
- Downcase/normalize nothing — keep the RRULE token in the exact RFC-5545 BYDAY form before calling RruleHelper
Example fix
// before "FREQ=MONTHLY;INTERVAL=1;BYDAY=MONDAY" // after "FREQ=MONTHLY;INTERVAL=1;BYDAY=2MO"
Defensive patterns
Strategy: validation
Validate before calling
raise ArgumentError, "invalid BYDAY" unless byday.split(",").all? { |d| d.match?(/\A[-+]?\d+(MO|TU|WE|TH|FR|SA|SU)\z|\A(MO|TU|WE|TH|FR|SA|SU)\z/) } Try / catch
begin
RruleHelper.rrule_to_natural_language(rropts)
rescue RruleValidationError => e
render json: { errors: [e.message] }, status: :bad_request
end Prevention
- Use RFC-5545 two-letter uppercase weekday codes
- Use signed integer prefixes (2MO, -1FR) for nth-weekday
- Do not lowercase or expand weekday tokens when generating rules
When it happens
Trigger: Calling rrule_to_natural_language with a monthly/yearly RRULE whose BYDAY contains items like "MON", "mo" (lowercase), "1stMO", "MO,WEDNESDAY", or any malformed token that does not match the pattern.
Common situations: Clients use full weekday names instead of two-letter codes; lowercase weekday codes from hand-built RRULEs; occurrence ordinals formatted like '-1st' or 'week=2'; case-mangling from other tooling lowercasing the rule string.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
AI-assisted analysis of instructure/canvas-lms@1c9f0bb801 (2026-09-15).
Data as JSON: /api/errors/48227bc3c386c174.
Report an issue: GitHub.
Appendix: source
Thrown at app/helpers/rrule_helper.rb:121
def byday_to_days(byday)
byday.split(/\s*,\s*/).map { |d| DAYS_OF_WEEK[d] }.join(", ")
end
def bymonth_to_month(bymonth)
MONTHS[bymonth]
end
# days is array of string digits
# e.g. ["1","15"]
def join_month_dys(days)
days.join(",")
end
def parse_byday(byday)
byday.split(",").map do |d|
match = /\A([-+]?\d+)?([A-Z]{2})\z/.match(d)
raise RruleValidationError, I18n.t("Invalid BYDAY '%{byday}'", byday:) unless match
{
occurrence: match[1].to_i,
day_of_week: DAYS_OF_WEEK[match[2]]
}
end
end
def parse_bymonth(bymonth)
month = bymonth.to_i
raise RruleValidationError, I18n.t("Invalid BYMONTH '%{bymonth}'", bymonth:) unless month.between?(1, 12)
month
end
def parse_bymonthday(bymonthday, month)
raise RruleValidationError, I18n.t("Unsupported BYMONTHDAY, only a single day is permitted.") unless bymonthday.split(",").length == 1
View on GitHub (pinned to 1c9f0bb801)