javan/whenever · error · ArgumentError
#{must_be_between}, #{at.min} given
Error message
#{must_be_between}, #{at.min} given What it means
For numeric frequencies, Whenever validates the :at option against the cron field it controls using range_or_integer. When :at is a Ruby Range whose start (at.min) falls outside the field's legal values, it raises ArgumentError naming the field, its bounds, and the offending min. Field bounds: Minute 0-59 (hourly jobs), Hour 0-23 (daily jobs), Day 1-31 (monthly jobs), Month 1-12 (yearly jobs).
Source
Thrown at lib/whenever/cron.rb:156
timing = Array.new(4, '*')
timing[0] = @at.is_a?(Time) ? @at.min : 0
timing[1] = @at.is_a?(Time) ? @at.hour : 0
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)).roundView on GitHub (pinned to 756163ed1a)
Solutions
- Clamp the Range start inside the field bounds: minutes 0-59, hours 0-23, days 1-31, months 1-12.
- If you mean a wall-clock time, pass a Chronic-parseable string such as at: '4pm' instead of a numeric Range — Whenever extracts hour/minute from the parsed Time.
- Validate generated schedules in CI with a bare `whenever` run so bad ranges fail the build, not the deploy.
Example fix
# before (day-of-month 0 is invalid in cron) every 1.month, at: (0..15) do rake 'billing:run' end # after every 1.month, at: (1..15) do rake 'billing:run' end
Defensive patterns
Strategy: validation
Validate before calling
LIMITS = { minute: 0..59, hour: 0..23, day: 1..31, month: 1..12 }
# before: every 1.month, at: (0..15)
at = (0..15); field = :day
unless LIMITS[field].cover?(at.min)
abort(field.to_s + ' range must start within ' + LIMITS[field].to_s)
end Type guard
def valid_at_range?(at, field)
limits = { minute: 0..59, hour: 0..23, day: 1..31, month: 1..12 }
at.is_a?(Range) && limits[field].cover?(at.min) && limits[field].cover?(at.max)
end Try / catch
begin
Whenever.cron(file: 'config/schedule.rb')
rescue ArgumentError => e
abort('invalid :at range in schedule: ' + e.message)
end Prevention
- Memorize the field bounds: minute 0-59, hour 0-23, day 1-31, month 1-12.
- Prefer Chronic strings like at: '4pm' over hand-built numeric ranges.
- Validate generated schedules in CI with a dry-run so bad ranges fail the build.
When it happens
Trigger: every 1.month, at: (0..15) → Day must be between 1-31, 0 given (day-of-month starts at 1 in cron). every 1.day, at: (25..27) → Hour must be between 0-23, 25 given. every 1.hour, at: (-5..10) → Minute must be between 0-59, -5 given.
Common situations: Applying 0-based thinking to days (0 is not a valid day-of-month) or forgetting hours cap at 23; programmatically generated ranges like (start..start + hours) that can overshoot bounds; timezone or duration math producing an endpoint like 24 for 'end of day'.
Related errors
- #{must_be_between}, #{at.max} given
- 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} given
AI-assisted analysis of javan/whenever@756163ed1a (2026-08-21).
Data as JSON: /api/errors/6206fc5a79f95a1e.
Report an issue: GitHub.