ruby-concurrency/concurrent-ruby · error · ArgumentError
number of threads must be greater than zero
Error message
number of threads must be greater than zero
What it means
Concurrent::FixedThreadPool.new(num_threads) pins min_threads = max_threads = num_threads. It raises ArgumentError when num_threads.to_i < 1. Because the value is coerced with to_i, nil (nil.to_i == 0), non-numeric strings ('abc'.to_i == 0), 0, and negatives all fail the check.
Source
Thrown at lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb:214
# The API and behavior of this class are based on Java's `FixedThreadPool`
#
# @!macro thread_pool_options
class FixedThreadPool < ThreadPoolExecutor
# @!macro fixed_thread_pool_method_initialize
#
# Create a new thread pool.
#
# @param [Integer] num_threads the number of threads to allocate
# @param [Hash] opts the options defining pool behavior.
# @option opts [Symbol] :fallback_policy (`:abort`) the fallback policy
#
# @raise [ArgumentError] if `num_threads` is less than or equal to zero
# @raise [ArgumentError] if `fallback_policy` is not a known policy
#
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool-int-
def initialize(num_threads, opts = {})
raise ArgumentError.new('number of threads must be greater than zero') if num_threads.to_i < 1
defaults = { max_queue: DEFAULT_MAX_QUEUE_SIZE,
idletime: DEFAULT_THREAD_IDLETIMEOUT }
overrides = { min_threads: num_threads,
max_threads: num_threads }
super(defaults.merge(opts).merge(overrides))
end
end
end
View on GitHub (pinned to 0b88d5ff75)
Solutions
- Strictly convert and validate before constructing: size = Integer(ENV.fetch('POOL_SIZE', 5)); raise ArgumentError if size < 1
- Default missing config to a sane positive value instead of nil or 0
- Treat a 'disabled' intent as not creating the pool at all rather than passing 0
- Use Integer() (raises on junk) instead of relying on to_i coercion
Example fix
# before
pool = Concurrent::FixedThreadPool.new(ENV['POOL_SIZE'])
# after
size = Integer(ENV.fetch('POOL_SIZE', 5))
raise ArgumentError, "POOL_SIZE must be >= 1, got #{size}" if size < 1
pool = Concurrent::FixedThreadPool.new(size) Defensive patterns
Strategy: validation
Validate before calling
size = Integer(env_value.nil? || env_value.empty? ? 5 : env_value)
raise ArgumentError, "pool size must be >= 1, got #{size}" if size < 1
pool = Concurrent::FixedThreadPool.new(size) Type guard
def valid_fixed_pool_size?(v) v.is_a?(Numeric) && v.to_i >= 1 end
Try / catch
begin Concurrent::FixedThreadPool.new(size) rescue ArgumentError => e raise unless e.message == 'number of threads must be greater than zero' Concurrent::FixedThreadPool.new(DEFAULT_POOL_SIZE) end
Prevention
- Use Integer() with fallback for ENV/config parsing instead of raw to_i
- Never pass 0 or nil to mean 'disabled': skip pool creation instead
- Unit-test pool sizing with unset, empty, and non-numeric config values
When it happens
Trigger: Concurrent::FixedThreadPool.new(0) or .new(-3); .new(nil); .new(ENV['POOL_SIZE']) where the variable is unset or non-numeric; sizing arithmetic that yields zero (count * 0).
Common situations: Pool size read from ENV or YAML that is missing in the deployed environment; 0 used to mean 'disabled'; string sizes from CLI flags ('4' works, '' or 'four' raises); arithmetic on unknown core counts producing zero or negative values.
Related errors
- #{@fallback_policy} is not a valid fallback policy
- `synchronous` cannot be set unless `max_queue` is 0
- `max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}
- `max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE
- `min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/f948af7fc88b6c7a.
Report an issue: GitHub.