{"record":{"id":"45b8668d0c037381","repo":"javan/whenever","slug":"time-must-be-in-minutes-or-higher","errorCode":null,"errorMessage":"Time must be in minutes or higher","messagePattern":"Time must be in minutes or higher","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/whenever/cron.rb","lineNumber":93,"sourceCode":"        if shortcut.is_a?(Numeric)\n          @time = shortcut\n          parse_time\n        elsif shortcut\n          if @at.is_a?(Time) || (@at.is_a?(Numeric) && @at > 0)\n            raise ArgumentError, \"You cannot specify an ':at' when using the shortcuts for times.\"\n          else\n            return shortcut\n          end\n        else\n          parse_as_string\n        end\n      end\n\n      def parse_time\n        timing = Array.new(5, '*')\n        case @time\n          when Whenever.seconds(0, :seconds)...Whenever.seconds(1, :minute)\n            raise ArgumentError, \"Time must be in minutes or higher\"\n          when Whenever.seconds(1, :minute)...Whenever.seconds(1, :hour)\n            minute_frequency = @time / 60\n            timing[0] = comma_separated_timing(minute_frequency, 59, @at || 0)\n          when Whenever.seconds(1, :hour)...Whenever.seconds(1, :day)\n            hour_frequency = (@time / 60 / 60).round\n            timing[0] = @at.is_a?(Time) ? @at.min : range_or_integer(@at, 0..59, 'Minute')\n            timing[1] = comma_separated_timing(hour_frequency, 23)\n          when Whenever.seconds(1, :day)...Whenever.seconds(1, :month)\n            day_frequency = (@time / 24 / 60 / 60).round\n            timing[0] = @at.is_a?(Time) ? @at.min  : 0\n            timing[1] = @at.is_a?(Time) ? @at.hour : range_or_integer(@at, 0..23, 'Hour')\n            timing[2] = comma_separated_timing(day_frequency, 31, 1)\n          when Whenever.seconds(1, :month)...Whenever.seconds(1, :year)\n            month_frequency = (@time / 30 / 24 / 60 / 60).round\n            timing[0] = @at.is_a?(Time) ? @at.min  : 0\n            timing[1] = @at.is_a?(Time) ? @at.hour : 0\n            timing[2] = if @at.is_a?(Time)\n              day_given? ? @at.day : 1","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/javan/whenever/blob/756163ed1aa928c8abd629aa2f4904bc5226538c/lib/whenever/cron.rb#L75-L111","documentation":"Whenever converts numeric frequencies into cron fields, and crontab syntax has no seconds field — its finest granularity is one minute. parse_time raises ArgumentError for any frequency below 60 seconds because such an interval simply cannot be expressed in cron. The check applies to the seconds range 0...60 after symbol-to-seconds conversion.","triggerScenarios":"every 30.seconds or every 5.seconds in config/schedule.rb (ActiveSupport durations are converted with to_i in time_in_cron_syntax); a raw integer below 60 such as every 45, which is interpreted as seconds; a computed frequency like every [jobs_count, 59].min that can land below 60.","commonSituations":"Porting a script that polled every 30 seconds via a sleep loop or a background-job interval into cron; new users assuming the every DSL supports second-level granularity; tightening an interval during load testing and hitting cron's floor.","solutions":["Raise the interval to at least one minute: every 1.minute.","If sub-minute execution is required, move the job out of cron: a daemon/loop, Sidekiq or Resque scheduled jobs, or a wrapper script that loops with sleep and is itself triggered by a minutely cron entry.","Run the job every minute and throttle inside the task (no-op if the last run was fewer than 30 seconds ago)."],"exampleFix":"# before\nevery 30.seconds do\n  runner 'Heartbeat.ping'\nend\n\n# after\nevery 1.minute do\n  runner 'Heartbeat.ping'\nend","handlingStrategy":"type-guard","validationCode":"interval = 30\nabort('cron intervals must be 60 seconds (1.minute) or higher') if interval.is_a?(Numeric) && interval < 60\nevery interval","typeGuard":"def cron_schedulable?(seconds)\n  seconds.is_a?(Numeric) && seconds.to_i >= 60\nend","tryCatchPattern":"begin\n  Whenever.cron(file: 'config/schedule.rb')\nrescue ArgumentError => e\n  abort('invalid interval in schedule: ' + e.message)\nend","preventionTips":["Remember cron is minute-granular by design; anything faster needs a daemon or background-job scheduler.","Validate intervals with a guard (>= 60) when schedules are generated from config or user input.","Include a schedule dry-run (`whenever` with no flags) in CI to catch sub-minute intervals before deploy."],"tags":["ruby","cron","scheduling","granularity","argumenterror"],"backgroundTag":"cron-sub-minute-interval","analyzedSha":"756163ed1aa928c8abd629aa2f4904bc5226538c","analyzedAt":"2026-08-21T18:07:39.974Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}