{"record":{"id":"5c0af1b4a2c9deb2","repo":"ruby-concurrency/concurrent-ruby","slug":"max-threads-cannot-be-less-than-default-min-po","errorCode":null,"errorMessage":"`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}","messagePattern":"`max_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":118,"sourceCode":"      end\n\n      # @!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,","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb#L100-L136","documentation":"On JRuby, ThreadPoolExecutor coerces max_threads with to_i and raises ArgumentError when it is below DEFAULT_MIN_POOL_SIZE (0), i.e. when it is negative. This guards the java.util.concurrent.ThreadPoolExecutor constructor from impossible pool sizes.","triggerScenarios":"max_threads: -1 (or any negative) on JRuby; string '-8' coerced by to_i; capacity math like available_cores - 2 when the core count is unknown/under-reported and the expression goes negative.","commonSituations":"Sizing formulas based on CPU detection that return negative values in containers or CI; negative sentinels meaning 'unbounded'; sign typos in config.","solutions":["Validate before constructing: n = Integer(opts[:max_threads]); raise if n < 0","Clamp computed sizes: [[computed, 0].max, 2_147_483_647].min","Use a positive default when core detection fails instead of propagating a negative"],"exampleFix":"# before\nConcurrent::ThreadPoolExecutor.new(max_threads: detected_cores - 2)\n# after\nmax_threads = [[detected_cores - 2, 0].max, 2_147_483_647].min\nConcurrent::ThreadPoolExecutor.new(max_threads: max_threads)","handlingStrategy":"validation","validationCode":"max_threads = Integer(cfg[:max_threads])\nraise ArgumentError, \"max_threads must be >= 0, got #{max_threads}\" if max_threads.negative?\nConcurrent::ThreadPoolExecutor.new(max_threads: max_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(max_threads: n)\nrescue ArgumentError => e\n  raise unless e.message.start_with?('`max_threads` cannot be less than')\n  Concurrent::ThreadPoolExecutor.new(max_threads: [n, 0].max)\nend","preventionTips":["Clamp ENV/core-count derived sizes: [computed, 0].max","Use Integer() parsing so junk values fail loudly at the config boundary","Add a config spec asserting thread counts are non-negative"],"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"}