javan/whenever · warning

[fail] Can't cut negative lines from the crontab #{options[:

Error message

[fail] Can't cut negative lines from the crontab #{options[:cut]}

What it means

The --cut/-k option strips N lines from the top of the existing crontab before updating (used to drop headers such as 'DO NOT EDIT THIS FILE'). The guard at command_line.rb:28-31 intends to reject negative cut values, but its regex /[0-9]*/ matches zero-or-more digits and therefore matches ANY string, including '-3' and the empty string — so as shipped this warning can never fire. Additionally, bin/whenever converts --cut to an Integer via lines.to_i before the check, so a negative value slips through and silently slices from the end of the crontab instead (split(...)[-3..-1] keeps the last 3 lines).

Source

Thrown at lib/whenever/command_line.rb:29

      @options[:crontab_command] ||= 'crontab'
      @options[:file]            ||= 'config/schedule.rb'
      @options[:cut]             ||= 0
      @options[:identifier]      ||= default_identifier
      @options[:console]    = true if @options[:console].nil?

      if !File.exist?(@options[:file]) && @options[:clear].nil?
        warn("[fail] Can't find file: #{@options[:file]}")
        return_or_exit(false)
      end

      if [@options[:update], @options[:write], @options[:clear]].compact.length > 1
        warn("[fail] Can only update, write or clear. Choose one.")
        return_or_exit(false)
      end

      unless @options[:cut].to_s =~ /[0-9]*/
        warn("[fail] Can't cut negative lines from the crontab #{options[:cut]}")
        return_or_exit(false)
      end
      @options[:cut] = @options[:cut].to_i

      @timestamp = Time.now.to_s
    end

    def run
      if @options[:update] || @options[:clear]
        write_crontab(updated_crontab)
      elsif @options[:write]
        write_crontab(whenever_cron)
      else
        puts Whenever.cron(@options)
        puts "## [message] Above is your schedule file converted to cron syntax; your crontab file was not updated."
        puts "## [message] Run `whenever --help' for more options."
        return_or_exit(true)
      end

View on GitHub (pinned to 756163ed1a)

Solutions

  1. Pass a non-negative integer to --cut / :cut (0 or more lines).
  2. Clamp computed values: use [0, computed_cut].max in the script that builds the command.
  3. If you maintain a fork, fix the guard to /\A\d+\z/ so invalid values are actually rejected instead of silently accepted.

Example fix

# before (goes negative on short crontabs)
whenever --update-crontab -k $((total_lines - keep))

# after
whenever --update-crontab -k $(( total_lines - keep > 0 ? total_lines - keep : 0 ))
Defensive patterns

Strategy: validation

Validate before calling

cut = Integer(value) rescue nil
abort('--cut must be a non-negative integer') unless cut && cut >= 0

Type guard

def valid_cut?(value)
  value.is_a?(Integer) && value >= 0
end

Try / catch

result = Whenever::CommandLine.execute(opts.merge(console: false))
abort('whenever rejected the cut option') unless result.zero?

Prevention

When it happens

Trigger: Computing the cut dynamically as (total_lines - lines_to_keep), which goes negative on short crontabs; passing user-supplied values to -k; running patched or vendored whenever builds where the regex was corrected to something like /\A\d+\z/, in which case cut: '-3' does trigger the warning and exit 1.

Common situations: Scripts migrating system crontabs (stripping the 3-line Debian 'DO NOT EDIT' header) where the line count assumption breaks; automation that derives cut from wc -l output; forks or vendor copies with a stricter guard than upstream.

Related errors


AI-assisted analysis of javan/whenever@756163ed1a (2026-08-21). Data as JSON: /api/errors/d88c7ddfbb00cc6a. Report an issue: GitHub.