ruby-concurrency/concurrent-ruby · error · ArgumentError
`synchronous` cannot be set unless `max_queue` is 0
Error message
`synchronous` cannot be set unless `max_queue` is 0
What it means
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.
Source
Thrown at lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb:117
super && !@executor.isTerminating
end
# @!macro thread_pool_executor_method_prune_pool
def prune_pool
deprecated "#prune_pool has no effect and will be removed in the next release."
end
private
def ns_initialize(opts)
min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i
max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
@max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
@synchronous = opts.fetch(:synchronous, DEFAULT_SYNCHRONOUS)
@fallback_policy = opts.fetch(:fallback_policy, :abort)
raise ArgumentError.new("`synchronous` cannot be set unless `max_queue` is 0") if @synchronous && @max_queue > 0
raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if max_length < DEFAULT_MIN_POOL_SIZE
raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if max_length > DEFAULT_MAX_POOL_SIZE
raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if min_length < DEFAULT_MIN_POOL_SIZE
raise ArgumentError.new("`min_threads` cannot be more than `max_threads`") if min_length > max_length
raise ArgumentError.new("#{fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICY_CLASSES.include?(@fallback_policy)
if @max_queue == 0
if @synchronous
queue = java.util.concurrent.SynchronousQueue.new
else
queue = java.util.concurrent.LinkedBlockingQueue.new
end
else
queue = java.util.concurrent.LinkedBlockingQueue.new(@max_queue)
end
@executor = java.util.concurrent.ThreadPoolExecutor.new(
min_length,View on GitHub (pinned to 0b88d5ff75)
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
Example fix
# before Concurrent::ThreadPoolExecutor.new(max_queue: 100, synchronous: true) # after Concurrent::ThreadPoolExecutor.new(synchronous: true) # max_queue defaults to 0
Defensive patterns
Strategy: validation
Validate before calling
opts = { synchronous: true }
opts[:max_queue] = 0 # synchronous hand-off requires an unqueueable pool
raise ArgumentError, 'max_queue must be 0 when synchronous' if opts[:synchronous] && opts.fetch(:max_queue, 0) > 0
Concurrent::ThreadPoolExecutor.new(**opts) Try / catch
begin
Concurrent::ThreadPoolExecutor.new(**pool_opts)
rescue ArgumentError => e
raise unless e.message.include?('`synchronous` cannot be set unless `max_queue` is 0')
pool_opts = pool_opts.merge(max_queue: 0)
retry
end Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- `max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}
- `max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE
- `min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}
- `min_threads` cannot be more than `max_threads`
- #{fallback_policy} is not a valid fallback policy
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/886ca38605ac6c19.
Report an issue: GitHub.