{"record":{"id":"4397887acb6f726b","repo":"ruby-concurrency/concurrent-ruby","slug":"synchronous-cannot-be-set-unless-max-queue-is-439788","errorCode":null,"errorMessage":"`synchronous` cannot be set unless `max_queue` is 0","messagePattern":"`synchronous` cannot be set unless `max_queue` is 0","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb","lineNumber":154,"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 next the release, see https://github.com/ruby-concurrency/concurrent-ruby/pull/1082.\"\n    end\n\n    private\n\n    # @!visibility private\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(\"#{@fallback_policy} is not a valid fallback policy\") unless FALLBACK_POLICIES.include?(@fallback_policy)\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\n      @pool                 = [] # all workers\n      @ready                = [] # used as a stash (most idle worker is at the start)\n      @queue                = [] # used as queue\n      # @ready or @queue is empty at all times\n      @scheduled_task_count = 0\n      @completed_task_count = 0\n      @largest_length       = 0\n      @workers_counter      = 0\n      @ruby_pid             = $$ # detects if Ruby has forked\n    end\n\n    # @!visibility private","sourceCodeStart":136,"sourceCodeEnd":172,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb#L136-L172","documentation":"On MRI, ThreadPoolExecutor with synchronous: true posts tasks by direct hand-off to a worker (no buffering, mirroring a synchronous queue). Since there is no queue to size, ns_initialize raises ArgumentError when synchronous is true while max_queue > 0. The default max_queue is 0, so the error requires both options together.","triggerScenarios":"Concurrent::ThreadPoolExecutor.new(max_queue: 100, synchronous: true) on MRI; adding synchronous: true to an existing bounded-queue config; config deep-merge combining a max_queue layer with a synchronous layer.","commonSituations":"Wanting bounded-queue backpressure AND direct hand-off at once, which is contradictory; shared config presets that mix incompatible options; upgrading pool settings without removing the now-conflicting max_queue.","solutions":["Drop synchronous: true if a bounded queue is wanted","Set max_queue: 0 (or omit it) for synchronous hand-off","Validate the pair before construction and keep the two options mutually exclusive in config schemas"],"exampleFix":"# before\nConcurrent::ThreadPoolExecutor.new(max_queue: 100, synchronous: true)\n# after\nConcurrent::ThreadPoolExecutor.new(max_queue: 100)","handlingStrategy":"validation","validationCode":"opts = cfg.slice(:min_threads, :max_threads, :idletime, :synchronous, :fallback_policy)\nraise ArgumentError, 'max_queue must be 0 when synchronous is true' if opts[:synchronous] && opts[:max_queue].to_i > 0\nConcurrent::ThreadPoolExecutor.new(**opts)","typeGuard":null,"tryCatchPattern":"begin\n  Concurrent::ThreadPoolExecutor.new(**pool_opts)\nrescue ArgumentError => e\n  raise unless e.message.include?('`synchronous` cannot be set unless `max_queue` is 0')\n  pool_opts = pool_opts.except(:max_queue)\n  retry\nend","preventionTips":["Model max_queue and synchronous as mutually exclusive in config schemas","Deep-merged YAML presets are a common source: audit merged results once at boot","Prefer bounded queue OR synchronous hand-off, never both"],"tags":["ruby","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"}