{"record":{"id":"c05a9c0d7433978a","repo":"ruby-concurrency/concurrent-ruby","slug":"value-cannot-be-negative-or-zero","errorCode":null,"errorMessage":"#{value} cannot be negative or zero","messagePattern":"#(.+?) cannot be negative or zero","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/utility/native_integer.rb","lineNumber":46,"sourceCode":"        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)\n        if value < 1\n          raise ArgumentError.new(\"#{value} cannot be negative or zero\")\n        end\n        value\n      end\n\n      extend self\n    end\n  end\nend\n","sourceCodeStart":28,"sourceCodeEnd":55,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/utility/native_integer.rb#L28-L55","documentation":"ensure_positive_and_no_zero requires values >= 1, raising ArgumentError('x cannot be negative or zero'). Its main caller is CyclicBarrier's constructor: a barrier needs at least one party for wait semantics to make sense, unlike Semaphore/CountDownLatch which accept zero. So CyclicBarrier.new(0) is explicitly rejected.","triggerScenarios":"Concurrent::CyclicBarrier.new(threads.size) when threads is empty; CyclicBarrier.new(0) used as a 'disabled barrier'; parties computed from config that is unset (nil -> type error) or 0.","commonSituations":"Sizing a barrier from a worker list or config value that can legitimately be empty; scaling configs where the minimum was assumed to be 1; test fixtures with degenerate zero-party barriers.","solutions":["Guard the degenerate case before constructing: skip barrier setup when parties < 1","Enforce a minimum at config load: parties = [cfg.fetch('parties', 1), 1].max","If a no-op barrier is needed, wrap: barrier = parties > 1 ? CyclicBarrier.new(parties) : nil and nil-check at wait sites"],"exampleFix":"# before (workers empty -> CyclicBarrier.new(0) -> ArgumentError)\nbarrier = Concurrent::CyclicBarrier.new(workers.size)\n\n# after\nbarrier = workers.size >= 1 ? Concurrent::CyclicBarrier.new(workers.size) : nil\nbarrier&.wait","handlingStrategy":"validation","validationCode":"raise ArgumentError, 'barrier needs at least 1 party' unless parties >= 1\nbarrier = Concurrent::CyclicBarrier.new(parties)","typeGuard":"def positive_int?(v)\n  v.is_a?(Integer) && v >= 1\nend","tryCatchPattern":null,"preventionTips":["Guard parties >= 1 before constructing; skip barrier setup for empty worker sets","Enforce a config minimum: parties = [cfg.fetch('parties', 1), 1].max","Cover the zero-party case in specs for barrier-based fan-out code"],"tags":["ruby","concurrency","cyclic-barrier","argument-error","validation"],"backgroundTag":"invalid-count-argument","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}