{"record":{"id":"cef59e437435a1f1","repo":"ruby-concurrency/concurrent-ruby","slug":"min-threads-cannot-be-less-than-default-min-po","errorCode":null,"errorMessage":"`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}","messagePattern":"`min_threads` cannot be less than #(.+?)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb","lineNumber":120,"sourceCode":"      # @!macro thread_pool_executor_method_prune_pool\n      def prune_pool\n        deprecated \"#prune_pool has no effect and will be removed in the next release.\"\n      end\n\n      private\n\n      def ns_initialize(opts)\n        min_length       = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i\n        max_length       = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i\n        idletime         = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i\n        @max_queue       = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i\n        @synchronous     = opts.fetch(:synchronous, DEFAULT_SYNCHRONOUS)\n        @fallback_policy = opts.fetch(:fallback_policy, :abort)\n\n        raise ArgumentError.new(\"`synchronous` cannot be set unless `max_queue` is 0\") if @synchronous && @max_queue > 0\n        raise ArgumentError.new(\"`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}\") if max_length < DEFAULT_MIN_POOL_SIZE\n        raise ArgumentError.new(\"`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}\") if max_length > DEFAULT_MAX_POOL_SIZE\n        raise ArgumentError.new(\"`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}\") if min_length < DEFAULT_MIN_POOL_SIZE\n        raise ArgumentError.new(\"`min_threads` cannot be more than `max_threads`\") if min_length > max_length\n        raise ArgumentError.new(\"#{fallback_policy} is not a valid fallback policy\") unless FALLBACK_POLICY_CLASSES.include?(@fallback_policy)\n\n        if @max_queue == 0\n          if @synchronous\n            queue = java.util.concurrent.SynchronousQueue.new\n          else\n            queue = java.util.concurrent.LinkedBlockingQueue.new\n          end\n        else\n          queue = java.util.concurrent.LinkedBlockingQueue.new(@max_queue)\n        end\n\n        @executor = java.util.concurrent.ThreadPoolExecutor.new(\n            min_length,\n            max_length,\n            idletime,\n            java.util.concurrent.TimeUnit::SECONDS,","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb#L102-L138","documentation":"On JRuby, ThreadPoolExecutor coerces min_threads with to_i and raises ArgumentError when it is below DEFAULT_MIN_POOL_SIZE (0), i.e. negative. min_threads is the floor of warm workers the pool keeps alive, so a negative count is meaningless.","triggerScenarios":"min_threads: -1 on JRuby; string '-4' coerced by to_i; formulas like keep_alive - reserve that go negative in constrained environments.","commonSituations":"Environment-derived sizing that underflows in containers with tiny cgroup CPU quotas; config sign typos; negative sentinels intended to disable warm threads.","solutions":["Validate before constructing: raise if Integer(opts[:min_threads]) < 0","Clamp computed values: [computed, 0].max","Use 0 to mean 'no warm threads' instead of a negative sentinel"],"exampleFix":"# before\nConcurrent::ThreadPoolExecutor.new(min_threads: ENV.fetch('MIN', 1).to_i - 2)\n# after\nmin_threads = [ENV.fetch('MIN', 1).to_i - 2, 0].max\nConcurrent::ThreadPoolExecutor.new(min_threads: min_threads)","handlingStrategy":"validation","validationCode":"min_threads = Integer(cfg.fetch(:min_threads, 0))\nraise ArgumentError, \"min_threads must be >= 0, got #{min_threads}\" if min_threads.negative?\nConcurrent::ThreadPoolExecutor.new(min_threads: min_threads)","typeGuard":"def valid_thread_count?(v, min = 0, max = 2_147_483_647)\n  v.is_a?(Numeric) && v.to_i.between?(min, max)\nend","tryCatchPattern":"begin\n  Concurrent::ThreadPoolExecutor.new(min_threads: n)\nrescue ArgumentError => e\n  raise unless e.message.start_with?('`min_threads` cannot be less than')\n  Concurrent::ThreadPoolExecutor.new(min_threads: [n, 0].max)\nend","preventionTips":["Use 0 (not a negative sentinel) to mean 'no warm workers'","Clamp reservation-style formulas in containers","Validate the whole opts hash once at the config boundary"],"tags":["ruby","jruby","concurrency","thread-pool","argument-error","configuration"],"backgroundTag":"invalid-thread-pool-configuration","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}