{"record":{"id":"886ca38605ac6c19","repo":"ruby-concurrency/concurrent-ruby","slug":"synchronous-cannot-be-set-unless-max-queue-is","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/java_thread_pool_executor.rb","lineNumber":117,"sourceCode":"        super && !@executor.isTerminating\n      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,","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb#L99-L135","documentation":"On JRuby, ThreadPoolExecutor with synchronous: true hands each task directly to a worker via a java.util.concurrent.SynchronousQueue: a worker must accept the task at the instant it is posted. There is therefore no queue to size, and ns_initialize raises ArgumentError when synchronous is true while max_queue > 0. The default max_queue is 0, so the error only appears when both options are supplied together.","triggerScenarios":"Concurrent::ThreadPoolExecutor.new(max_queue: 100, synchronous: true) on JRuby; copying a bounded-queue config preset and adding synchronous: true; config merging where one layer sets max_queue and another adds synchronous.","commonSituations":"Trying to combine bounded-queue backpressure with direct hand-off semantics; shared YAML presets that mix incompatible options; upgrading a pool to synchronous mode without removing an existing max_queue setting.","solutions":["Remove synchronous: true if you want a bounded queue with backpressure","Set max_queue: 0 (or omit it) if you want synchronous hand-off","Split shared pool presets so max_queue and synchronous cannot co-occur; validate the pair before construction"],"exampleFix":"# before\nConcurrent::ThreadPoolExecutor.new(max_queue: 100, synchronous: true)\n# after\nConcurrent::ThreadPoolExecutor.new(synchronous: true)  # max_queue defaults to 0","handlingStrategy":"validation","validationCode":"opts = { synchronous: true }\nopts[:max_queue] = 0 # synchronous hand-off requires an unqueueable pool\nraise ArgumentError, 'max_queue must be 0 when synchronous' if opts[:synchronous] && opts.fetch(:max_queue, 0) > 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.merge(max_queue: 0)\n  retry\nend","preventionTips":["Treat max_queue and synchronous as mutually exclusive in config schemas","Review pool presets as a unit after any change to either option","Remember the default max_queue is 0: only explicit conflicting values trigger this"],"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"}