ruby-concurrency/concurrent-ruby · error · ArgumentError

no block given

Error message

no block given

What it means

SerializedExecutionDelegator wraps another executor so that submitted tasks run one at a time in submission order; its #post requires the task as a block. If you call post without a block (for example passing a Proc or Method as a positional argument), it raises ArgumentError 'no block given' before anything is queued.

Source

Thrown at lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb:23

module Concurrent

  # A wrapper/delegator for any `ExecutorService` that
  # guarantees serialized execution of tasks.
  #
  # @see [SimpleDelegator](http://www.ruby-doc.org/stdlib-2.1.2/libdoc/delegate/rdoc/SimpleDelegator.html)
  # @see Concurrent::SerializedExecution
  class SerializedExecutionDelegator < SimpleDelegator
    include SerialExecutorService

    def initialize(executor)
      @executor   = executor
      @serializer = SerializedExecution.new
      super(executor)
    end

    # @!macro executor_service_method_post
    def post(*args, &task)
      raise ArgumentError.new('no block given') unless block_given?
      return false unless running?
      @serializer.post(@executor, *args, &task)
    end
  end
end

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Pass the task as a block: delegator.post { do_work }
  2. If the task is a Proc/Method object, splat it as a block: delegator.post(&task)
  3. In wrapper methods, forward the block explicitly: def enqueue(&task) delegator.post(&task) end

Example fix

# before
task = ->(arg) { process(arg) }
delegator.post(task)

# after
task = ->(arg) { process(arg) }
delegator.post(&task)
Defensive patterns

Strategy: validation

Validate before calling

def enqueue(serializer, &task)
  raise ArgumentError, 'task block required' unless task
  serializer.post(&task)
end

Type guard

->(obj) { obj.respond_to?(:call) } # then post(&obj)

Try / catch

begin
  delegator.post(&task)
rescue ArgumentError => e
  raise ArgumentError, "enqueue rejected: #{e.message}" if /no block/.match?(e.message)
  raise
end

Prevention

When it happens

Trigger: delegator.post(task) where task is a Proc/Method instead of delegator.post(&task); forwarding a block through a wrapper method that forgets the &; calling post on an executor obtained from SerializedExecutionDelegator.new(executor) with the task in a variable.

Common situations: Wrapping a custom or global executor for serialized (one-at-a-time) execution and reusing callable objects built elsewhere; refactoring code that stored lambdas in variables; test doubles that call post with positional callables.

Related errors


AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21). Data as JSON: /api/errors/ea2b06c8ca388cfb. Report an issue: GitHub.