ruby-concurrency/concurrent-ruby · error · ArgumentError
must be greater than zero
Error message
must be greater than zero
What it means
TimerTask#execution_interval= rejects values that are <= 0 after to_f (unlike ScheduledTask, zero is not allowed here), raising ArgumentError('must be greater than zero'). The same setter runs during TimerTask.new via the :execution / :execution_interval option, so bad intervals surface at construction. Note nil.to_f == 0.0, so a missing config value raises this error. The companion timeout_interval= is deprecated and only warns.
Source
Thrown at lib/concurrent-ruby/concurrent/timer_task.rb:270
# task = Concurrent::TimerTask.execute(execution_interval: 10){ print "Hello World\n" }
# task.running? #=> true
def self.execute(opts = {}, &task)
TimerTask.new(opts, &task).execute
end
# @!attribute [rw] execution_interval
# @return [Fixnum] Number of seconds after the task completes before the
# task is performed again.
def execution_interval
synchronize { @execution_interval }
end
# @!attribute [rw] execution_interval
# @return [Fixnum] Number of seconds after the task completes before the
# task is performed again.
def execution_interval=(value)
if (value = value.to_f) <= 0.0
raise ArgumentError.new('must be greater than zero')
else
synchronize { @execution_interval = value }
end
end
# @!attribute [r] interval_type
# @return [Symbol] method to calculate the interval between executions
attr_reader :interval_type
# @!attribute [rw] timeout_interval
# @return [Fixnum] Number of seconds the task can run before it is
# considered to have failed.
def timeout_interval
warn 'TimerTask timeouts are now ignored as these were not able to be implemented correctly'
end
# @!attribute [rw] timeout_interval
# @return [Fixnum] Number of seconds the task can run before it isView on GitHub (pinned to 0b88d5ff75)
Solutions
- Default the value at the boundary: interval = Float(cfg.fetch('interval', 30))
- Treat 0/nil as 'do not start the timer' instead of passing it through: skip TimerTask.new unless interval && interval > 0
- Validate once at config load: raise unless interval.is_a?(Numeric) && interval > 0
Example fix
# before (nil from missing config becomes 0.0 and raises)
task = Concurrent::TimerTask.new(execution_interval: cfg['interval']) { sync }
# after (explicit default + explicit disable semantics)
interval = Float(cfg.fetch('interval', 30))
task = interval > 0 ? Concurrent::TimerTask.new(execution_interval: interval) { sync } : nil Defensive patterns
Strategy: validation
Validate before calling
interval = Float(interval)
raise ArgumentError, "interval #{interval} must be > 0" unless interval > 0
Concurrent::TimerTask.new(execution_interval: interval) { work } Type guard
def valid_interval?(v) v.is_a?(Numeric) && v.to_f > 0 end
Try / catch
begin
timer.execution_interval = new_interval
rescue ArgumentError
logger.warn("ignoring invalid interval #{new_interval.inspect}")
end Prevention
- Coerce and default intervals at the config boundary (Float(cfg.fetch('interval', DEFAULT)))
- Treat 0/nil as 'timer disabled' rather than passing it to TimerTask
- Remember nil.to_f == 0.0 — a missing key raises this exact error
When it happens
Trigger: TimerTask.new(execution_interval: 0) or execution_interval: -5; timer_task.execution_interval = 0; interval sourced from config/ENV that is nil, absent, or the string '0' ('0'.to_f == 0.0).
Common situations: Intervals read from YAML/ENV where 'disable the timer' was encoded as 0 or left blank; ops-configured intervals with typos or negative numbers; dividing to produce a Float that lands on 0.0.
Related errors
- no block given
- interval_type must be either :fixed_delay or :fixed_rate
- no block given
- Not all dependencies are IVars. Dependencies: #{ inputs.insp
- no block given
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/c965dd1e87be9a2c.
Report an issue: GitHub.