{"record":{"id":"9b7f33231df0c319","repo":"ruby-concurrency/concurrent-ruby","slug":"min-threads-cannot-be-more-than-max-threads","errorCode":null,"errorMessage":"`min_threads` cannot be more than `max_threads`","messagePattern":"`min_threads` cannot be more than `max_threads`","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb","lineNumber":121,"sourceCode":"      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,\n            queue,","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb#L103-L139","documentation":"On JRuby, ThreadPoolExecutor raises ArgumentError when min_threads exceeds max_threads. The pool would otherwise be unable to satisfy both bounds, so construction fails fast during ns_initialize.","triggerScenarios":"min_threads: 10, max_threads: 5 on JRuby; independently configured values (MIN defaulting high while MAX is lowered); YAML overrides applied in the wrong precedence order.","commonSituations":"Two ENV vars or YAML keys maintained by different teams drifting apart; lowering max_threads in an environment-specific override while min_threads comes from a shared default; renames that swap the two keys.","solutions":["Set max_threads first and derive min_threads from it: min = [wanted_min, max].min","Validate the pair before construction and raise with both values in the message","Keep both values in one config block so they are reviewed together"],"exampleFix":"# before\nConcurrent::ThreadPoolExecutor.new(min_threads: 10, max_threads: 5)\n# after\nmax_threads = 5\nmin_threads = [10, max_threads].min\nConcurrent::ThreadPoolExecutor.new(min_threads: min_threads, max_threads: max_threads)","handlingStrategy":"validation","validationCode":"max_threads = Integer(cfg.fetch(:max_threads, 2_147_483_647))\nmin_threads = [Integer(cfg.fetch(:min_threads, 0)), max_threads].min\nraise ArgumentError, \"min_threads #{min_threads} > max_threads #{max_threads}\" if min_threads > max_threads\nConcurrent::ThreadPoolExecutor.new(min_threads: min_threads, max_threads: max_threads)","typeGuard":"def coherent_pool_bounds?(min, max)\n  min.is_a?(Integer) && max.is_a?(Integer) && min >= 0 && max >= min && max <= 2_147_483_647\nend","tryCatchPattern":"begin\n  Concurrent::ThreadPoolExecutor.new(min_threads: mn, max_threads: mx)\nrescue ArgumentError => e\n  raise unless e.message.start_with?('`min_threads` cannot be more than')\n  Concurrent::ThreadPoolExecutor.new(min_threads: [mn, mx].min, max_threads: mx)\nend","preventionTips":["Derive min_threads from max_threads in config templates","Keep both keys in one reviewed config block","Add a spec asserting min <= max across environment overrides"],"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"}