ruby-concurrency/concurrent-ruby · error · RangeError

#{value} is less than the maximum value of #{MIN_VALUE}

Error message

#{value} is less than the maximum value of #{MIN_VALUE}

What it means

The lower-bound twin of the maximum check: values below -(2**62) cannot be held in a native fixnum slot, so storing them into AtomicFixnum (new, value=, update) or the count-taking constructors of Semaphore/CountDownLatch/CyclicBarrier raises RangeError. Ruby's arbitrary-precision arithmetic happily produces such values right up to the store.

Source

Thrown at lib/concurrent-ruby/concurrent/utility/native_integer.rb:19

module Concurrent
  # @!visibility private
  module Utility
    # @private
    module NativeInteger
      # http://stackoverflow.com/questions/535721/ruby-max-integer
      MIN_VALUE = -(2**(0.size * 8 - 2))
      MAX_VALUE = (2**(0.size * 8 - 2) - 1)

      def ensure_upper_bound(value)
        if value > MAX_VALUE
          raise RangeError.new("#{value} is greater than the maximum value of #{MAX_VALUE}")
        end
        value
      end

      def ensure_lower_bound(value)
        if value < MIN_VALUE
          raise RangeError.new("#{value} is less than the maximum value of #{MIN_VALUE}")
        end
        value
      end

      def ensure_integer(value)
        unless value.is_a?(Integer)
          raise ArgumentError.new("#{value} is not an Integer")
        end
        value
      end

      def ensure_integer_and_bounds(value)
        ensure_integer value
        ensure_upper_bound value
        ensure_lower_bound value
      end

      def ensure_positive(value)

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Validate before storing: raise RangeError, 'below native minimum' if value < Concurrent::Utility::NativeInteger::MIN_VALUE
  2. Clamp when undershoot has a defined floor: value = [value, Concurrent::Utility::NativeInteger::MIN_VALUE].max
  3. Move unbounded arithmetic into plain Integers + Mutex, using AtomicFixnum only where values provably stay in range

Example fix

# before
balance = Concurrent::AtomicFixnum.new(-(2**62) - 1) # RangeError

# after
min = Concurrent::Utility::NativeInteger::MIN_VALUE
raise RangeError, 'balance underflow' if amount < min
balance = Concurrent::AtomicFixnum.new(amount)
Defensive patterns

Strategy: validation

Validate before calling

NI = Concurrent::Utility::NativeInteger
raise RangeError, "#{value} below atomic counter range" if value.is_a?(Integer) && value < NI::MIN_VALUE
Concurrent::AtomicFixnum.new(value)

Type guard

def native_int?(v)
  v.is_a?(Integer) &&
    v.between?(Concurrent::Utility::NativeInteger::MIN_VALUE,
               Concurrent::Utility::NativeInteger::MAX_VALUE)
end

Try / catch

begin
  counter.value = next_value
rescue RangeError
  counter.value = Concurrent::Utility::NativeInteger::MIN_VALUE # explicit floor + alert
end

Prevention

When it happens

Trigger: Concurrent::AtomicFixnum.new(-(2**62)); counter.update { |v| v - 2**62 } from a subtraction of big aggregates; negating a large positive counter; semaphore/latch counts computed as differences of big numbers.

Common situations: Balance/credit arithmetic going deeply negative before a domain check runs; mirroring large external counters (negative deltas); JRuby-to-MRI porting where the native range differs.

Related errors


AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21). Data as JSON: /api/errors/0e7b49af46868fc1. Report an issue: GitHub.