{"record":{"id":"6206fc5a79f95a1e","repo":"javan/whenever","slug":"must-be-between-at-min-given","errorCode":null,"errorMessage":"#{must_be_between}, #{at.min} given","messagePattern":"#(.+?), #(.+?) given","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/whenever/cron.rb","lineNumber":156,"sourceCode":"\n        timing = Array.new(4, '*')\n        timing[0] = @at.is_a?(Time) ? @at.min  : 0\n        timing[1] = @at.is_a?(Time) ? @at.hour : 0\n\n        return (timing << '1-5') * \" \" if string.downcase.index('weekday')\n        return (timing << '6,0') * \" \" if string.downcase.index('weekend')\n\n        DAYS.each_with_index do |day, i|\n          return (timing << i) * \" \" if string.downcase.index(day)\n        end\n\n        raise ArgumentError, \"Couldn't parse: #{@time.inspect}\"\n      end\n\n      def range_or_integer(at, valid_range, name)\n        must_be_between = \"#{name} must be between #{valid_range.min}-#{valid_range.max}\"\n        if at.is_a?(Range)\n          raise ArgumentError, \"#{must_be_between}, #{at.min} given\" unless valid_range.include?(at.min)\n          raise ArgumentError, \"#{must_be_between}, #{at.max} given\" unless valid_range.include?(at.max)\n          return \"#{at.min}-#{at.max}\"\n        end\n        raise ArgumentError, \"#{must_be_between}, #{at} given\" unless valid_range.include?(at)\n        at\n      end\n\n      def comma_separated_timing(frequency, max, start = 0)\n        return start     if frequency.nil? || frequency == \"\" || frequency.zero?\n        return '*'       if frequency == 1\n        return frequency if frequency > (max * 0.5).ceil\n\n        original_start = start\n\n        start += frequency unless (max + 1).modulo(frequency).zero? || start > 0\n        output = (start..max).step(frequency).to_a\n\n        max_occurances = (max.to_f  / (frequency.to_f)).round","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/javan/whenever/blob/756163ed1aa928c8abd629aa2f4904bc5226538c/lib/whenever/cron.rb#L138-L174","documentation":"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).","triggerScenarios":"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.","commonSituations":"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'.","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."],"exampleFix":"# before (day-of-month 0 is invalid in cron)\nevery 1.month, at: (0..15) do\n  rake 'billing:run'\nend\n\n# after\nevery 1.month, at: (1..15) do\n  rake 'billing:run'\nend","handlingStrategy":"validation","validationCode":"LIMITS = { minute: 0..59, hour: 0..23, day: 1..31, month: 1..12 }\n# before: every 1.month, at: (0..15)\nat = (0..15); field = :day\nunless LIMITS[field].cover?(at.min)\n  abort(field.to_s + ' range must start within ' + LIMITS[field].to_s)\nend","typeGuard":"def valid_at_range?(at, field)\n  limits = { minute: 0..59, hour: 0..23, day: 1..31, month: 1..12 }\n  at.is_a?(Range) && limits[field].cover?(at.min) && limits[field].cover?(at.max)\nend","tryCatchPattern":"begin\n  Whenever.cron(file: 'config/schedule.rb')\nrescue ArgumentError => e\n  abort('invalid :at range in schedule: ' + e.message)\nend","preventionTips":["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."],"tags":["ruby","cron","scheduling","range-validation","argumenterror"],"backgroundTag":"cron-field-out-of-range","analyzedSha":"756163ed1aa928c8abd629aa2f4904bc5226538c","analyzedAt":"2026-08-21T18:07:39.974Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}