{"record":{"id":"ce61c4bdf29a2514","repo":"ruby-concurrency/concurrent-ruby","slug":"value-is-not-an-integer","errorCode":null,"errorMessage":"#{value} is not an Integer","messagePattern":"#(.+?) is not an Integer","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/utility/native_integer.rb","lineNumber":26,"sourceCode":"      MAX_VALUE = (2**(0.size * 8 - 2) - 1)\n\n      def ensure_upper_bound(value)\n        if value > MAX_VALUE\n          raise RangeError.new(\"#{value} is greater than the maximum value of #{MAX_VALUE}\")\n        end\n        value\n      end\n\n      def ensure_lower_bound(value)\n        if value < MIN_VALUE\n          raise RangeError.new(\"#{value} is less than the maximum value of #{MIN_VALUE}\")\n        end\n        value\n      end\n\n      def ensure_integer(value)\n        unless value.is_a?(Integer)\n          raise ArgumentError.new(\"#{value} is not an Integer\")\n        end\n        value\n      end\n\n      def ensure_integer_and_bounds(value)\n        ensure_integer value\n        ensure_upper_bound value\n        ensure_lower_bound value\n      end\n\n      def ensure_positive(value)\n        if value < 0\n          raise ArgumentError.new(\"#{value} cannot be negative\")\n        end\n        value\n      end\n\n      def ensure_positive_and_no_zero(value)","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/utility/native_integer.rb#L8-L44","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Coerce explicitly at the boundary: count = Integer(value) (Kernel#Integer raises on garbage rather than coercing)","For trusted numeric strings use value.to_i after a format check like /\\A-?\\d+\\z/","Check types in config loading: raise TypeError, 'expected Integer' unless v.is_a?(Integer)"],"exampleFix":"# before\nlatch = Concurrent::CountDownLatch.new(ENV['WORKERS'])          # String -> ArgumentError\nfix   = Concurrent::AtomicFixnum.new(json['offset'])            # String\n\n# after\nlatch = Concurrent::CountDownLatch.new(Integer(ENV['WORKERS'], 10))\nfix   = Concurrent::AtomicFixnum.new(Integer(json['offset'], 10))","handlingStrategy":"type-guard","validationCode":"count = Integer(value) # Kernel#Integer: strict, raises on garbage\nConcurrent::CountDownLatch.new(count)","typeGuard":"def strict_integer?(v)\n  v.is_a?(Integer)\nend","tryCatchPattern":"begin\n  latch = Concurrent::CountDownLatch.new(raw)\nrescue ArgumentError => e\n  raise unless e.message.include?('not an Integer')\n  latch = Concurrent::CountDownLatch.new(Integer(raw, 10))\nend","preventionTips":["Convert at boundaries with Kernel#Integer (strict) rather than relying on to_i everywhere","Freeze expected types in config schemas and validate on load","Remember 3.0 is rejected too — avoid float arithmetic feeding atomics"],"tags":["ruby","concurrency","atomic","argument-error","type-coercion"],"backgroundTag":"wrong-argument-type","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-22T04:17:13.399Z"}