javan/whenever · error · ArgumentError
You cannot specify an ':at' when using the shortcuts for tim
Error message
You cannot specify an ':at' when using the shortcuts for times.
What it means
Whenever maps the cron @ keywords (:reboot, :yearly, :annually, :monthly, :weekly, :daily, :midnight, :hourly) directly to raw @-syntax cron like @daily, which has no minute or hour field. Because there is nowhere to place a time of day, parse_symbol raises ArgumentError when :at is also given (any Chronic-parsed string yields a Time, and a numeric greater than 0 also triggers it). The interval symbols (:year, :month, :week, :day, :hour, :minute) are converted to seconds first and DO accept :at.
Source
Thrown at lib/whenever/cron.rb:80
end
def parse_symbol
shortcut = case @time
when *KEYWORDS then "@#{@time}" # :reboot => '@reboot'
when :year then Whenever.seconds(1, :year)
when :day then Whenever.seconds(1, :day)
when :month then Whenever.seconds(1, :month)
when :week then Whenever.seconds(1, :week)
when :hour then Whenever.seconds(1, :hour)
when :minute then Whenever.seconds(1, :minute)
end
if shortcut.is_a?(Numeric)
@time = shortcut
parse_time
elsif shortcut
if @at.is_a?(Time) || (@at.is_a?(Numeric) && @at > 0)
raise ArgumentError, "You cannot specify an ':at' when using the shortcuts for times."
else
return shortcut
end
else
parse_as_string
end
end
def parse_time
timing = Array.new(5, '*')
case @time
when Whenever.seconds(0, :seconds)...Whenever.seconds(1, :minute)
raise ArgumentError, "Time must be in minutes or higher"
when Whenever.seconds(1, :minute)...Whenever.seconds(1, :hour)
minute_frequency = @time / 60
timing[0] = comma_separated_timing(minute_frequency, 59, @at || 0)
when Whenever.seconds(1, :hour)...Whenever.seconds(1, :day)
hour_frequency = (@time / 60 / 60).roundView on GitHub (pinned to 756163ed1a)
Solutions
- Replace the keyword with an interval: every 1.day, at: '4pm' (or the equivalent symbols :day, :hour, :week, :month, :year).
- Drop the :at option and accept the @-shortcut's fixed cron time (e.g. @daily runs at midnight).
- Use raw cron syntax for full control: every '0 16 * * *' for 4pm daily.
- If generating schedules programmatically, check Whenever::Output::Cron::KEYWORDS before attaching :at.
Example fix
# before every :daily, at: '4:30pm' do rake 'logs:clear' end # after every 1.day, at: '4:30pm' do rake 'logs:clear' end
Defensive patterns
Strategy: validation
Validate before calling
AT_KEYWORDS = %i[reboot yearly annually monthly weekly daily midnight hourly]
if AT_KEYWORDS.include?(frequency) && options.key?(:at)
abort('at: cannot be combined with the ' + frequency.inspect + ' shortcut; use an interval such as 1.day instead')
end
every frequency, options Type guard
def accepts_at?(time) !%i[reboot yearly annually monthly weekly daily midnight hourly].include?(time) end
Try / catch
begin
Whenever.cron(file: 'config/schedule.rb')
rescue ArgumentError => e
abort('config/schedule.rb invalid: ' + e.message)
end Prevention
- Reserve @ keyword shortcuts for jobs without a time of day; use interval values (1.day, :hour) whenever :at is needed.
- Run a bare `whenever` in CI — it prints the converted schedule without writing the crontab and fails fast on DSL errors.
- Keep a unit test that evaluates the schedule file so refactors from 1.day to :daily with a leftover :at break the build.
When it happens
Trigger: every :daily, at: '4pm'; every :hourly, at: 30; every :midnight, at: '3:00am' — any symbol in Whenever::Output::Cron::KEYWORDS combined with a truthy :at. Contrast: every :day, at: '4pm' does NOT raise, because :day becomes 86400 seconds and goes through parse_time. A string :at is Chronic-parsed in the constructor, so even '00:00' becomes a Time and triggers the raise.
Common situations: Developers with a cron background writing every :daily, at: ... since @daily is familiar syntax; refactoring every 1.day, at: ... to the shorter :daily and keeping the :at option; copy-pasting examples that mix @ keywords with :at; schedule generators that attach :at unconditionally to every job.
Related errors
- Couldn't parse: #{@time.inspect}
- Time must be in minutes or higher
- #{must_be_between}, #{at.min} given
- #{must_be_between}, #{at.max} given
- #{must_be_between}, #{at} given
AI-assisted analysis of javan/whenever@756163ed1a (2026-08-21).
Data as JSON: /api/errors/860e63ddea877a44.
Report an issue: GitHub.