ruby-concurrency/concurrent-ruby · error · ArgumentError

#{value} is not an Integer

Error message

#{value} is not an Integer

What it means

The native-integer guard requires actual Integer inputs: anything else (String, Float, BigDecimal, nil) raises ArgumentError('x is not an Integer'). It runs in AtomicFixnum.new/#value=/#update and in the constructors of Semaphore, CountDownLatch, and CyclicBarrier. Note that even whole-number Floats like 3.0 are rejected — there is no implicit coercion.

Source

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

      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)
        if value < 0
          raise ArgumentError.new("#{value} cannot be negative")
        end
        value
      end

      def ensure_positive_and_no_zero(value)

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Coerce explicitly at the boundary: count = Integer(value) (Kernel#Integer raises on garbage rather than coercing)
  2. For trusted numeric strings use value.to_i after a format check like /\A-?\d+\z/
  3. Check types in config loading: raise TypeError, 'expected Integer' unless v.is_a?(Integer)

Example fix

# before
latch = Concurrent::CountDownLatch.new(ENV['WORKERS'])          # String -> ArgumentError
fix   = Concurrent::AtomicFixnum.new(json['offset'])            # String

# after
latch = Concurrent::CountDownLatch.new(Integer(ENV['WORKERS'], 10))
fix   = Concurrent::AtomicFixnum.new(Integer(json['offset'], 10))
Defensive patterns

Strategy: type-guard

Validate before calling

count = Integer(value) # Kernel#Integer: strict, raises on garbage
Concurrent::CountDownLatch.new(count)

Type guard

def strict_integer?(v)
  v.is_a?(Integer)
end

Try / catch

begin
  latch = Concurrent::CountDownLatch.new(raw)
rescue ArgumentError => e
  raise unless e.message.include?('not an Integer')
  latch = Concurrent::CountDownLatch.new(Integer(raw, 10))
end

Prevention

When it happens

Trigger: Concurrent::AtomicFixnum.new('42') from ENV/JSON; CountDownLatch.new(3.0) after float division; semaphore = Semaphore.new(params['permits']) where params parsing yields a string; nil passed through a missing config key.

Common situations: Values parsed from YAML/JSON/ENV that arrive as strings; arithmetic that mixes Floats (n / 2.0, BigDecimal money math) and is stored into an atomic; user input forwarded without conversion.

Related errors


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