javan/whenever · error · ArgumentError
#{must_be_between}, #{at} given
Error message
#{must_be_between}, #{at} given What it means
Whenever validates a numeric :at against the cron field it feeds: for hourly jobs the number is a minute (0-59), for daily jobs an hour (0-23), for monthly jobs a day (1-31), for yearly jobs a month (1-12). If the integer lies outside that window, range_or_integer raises ArgumentError with the field name, its bounds, and the value. Strings are not validated here — they go through Chronic first, so '4:30pm' works where 430 does not.
Source
Thrown at lib/whenever/cron.rb:160
return (timing << '1-5') * " " if string.downcase.index('weekday')
return (timing << '6,0') * " " if string.downcase.index('weekend')
DAYS.each_with_index do |day, i|
return (timing << i) * " " if string.downcase.index(day)
end
raise ArgumentError, "Couldn't parse: #{@time.inspect}"
end
def range_or_integer(at, valid_range, name)
must_be_between = "#{name} must be between #{valid_range.min}-#{valid_range.max}"
if at.is_a?(Range)
raise ArgumentError, "#{must_be_between}, #{at.min} given" unless valid_range.include?(at.min)
raise ArgumentError, "#{must_be_between}, #{at.max} given" unless valid_range.include?(at.max)
return "#{at.min}-#{at.max}"
end
raise ArgumentError, "#{must_be_between}, #{at} given" unless valid_range.include?(at)
at
end
def comma_separated_timing(frequency, max, start = 0)
return start if frequency.nil? || frequency == "" || frequency.zero?
return '*' if frequency == 1
return frequency if frequency > (max * 0.5).ceil
original_start = start
start += frequency unless (max + 1).modulo(frequency).zero? || start > 0
output = (start..max).step(frequency).to_a
max_occurances = (max.to_f / (frequency.to_f)).round
max_occurances += 1 if original_start.zero?
output[0, max_occurances].join(',')
endView on GitHub (pinned to 756163ed1a)
Solutions
- Pass times as parseable strings: at: '14:30' or at: '4:30pm' — Whenever runs them through Chronic and extracts hour and minute.
- If numeric, use only the field value: hour 0-23 for daily jobs, minute 0-59 for hourly jobs, day 1-31 for monthly, month 1-12 for yearly.
- Validate generated schedules with a dry-run `whenever` in CI so out-of-range values fail early.
Example fix
# before (Hour must be between 0-23, 1430 given) every 1.day, at: 1430 do rake 'daily:sync' end # after every 1.day, at: '14:30' do rake 'daily:sync' end
Defensive patterns
Strategy: validation
Validate before calling
LIMITS = { minute: 0..59, hour: 0..23, day: 1..31, month: 1..12 }
at = 1430; field = :hour # from: every 1.day, at: 1430
unless LIMITS[field].cover?(at)
abort('numeric :at must be a ' + field.to_s + ' (0-23 for daily jobs); use a string like 14:30 for clock times')
end Type guard
def valid_at_integer?(at, field)
limits = { minute: 0..59, hour: 0..23, day: 1..31, month: 1..12 }
at.is_a?(Integer) && limits[field].cover?(at)
end Try / catch
begin
Whenever.cron(file: 'config/schedule.rb')
rescue ArgumentError => e
abort('invalid numeric :at in schedule (pass clock times as strings): ' + e.message)
end Prevention
- Pass clock times as strings ('14:30', '4:30pm') — Chronic parses them and extracts hour and minute.
- For numeric at, remember which field it feeds: minute for hourly jobs, hour for daily, day for monthly, month for yearly.
- Never pass compacted military-time integers like 1430; they are validated as raw field values.
When it happens
Trigger: every 1.hour, at: 75 → Minute must be between 0-59, 75 given. every 1.day, at: 430 (meaning 4:30) → Hour must be between 0-23, 430 given. every 1.year, at: 13 → Month must be between 1-12, 13 given. Any compacted military-time integer like 1430 passed as a number instead of a string.
Common situations: Passing clock times as integers (430, 1430) because they look numeric; scripts generating at values from timestamps or arithmetic that overshoot field bounds; mixing up which field the number applies to for each frequency (minute for hourly, hour for daily).
Related errors
- You cannot specify an ':at' when using the shortcuts for tim
- Time must be in minutes or higher
- Couldn't parse: #{@time.inspect}
- #{must_be_between}, #{at.min} given
- #{must_be_between}, #{at.max} given
AI-assisted analysis of javan/whenever@756163ed1a (2026-08-21).
Data as JSON: /api/errors/25d30be32f308de4.
Report an issue: GitHub.