ruby-concurrency/concurrent-ruby · error · ArgumentError

no block given

Error message

no block given

What it means

ImmediateExecutor runs tasks synchronously on the caller's thread and exists mainly for testing and debugging. Its post(*args, &task) raises ArgumentError('no block given') when no block is supplied; the check runs before the running? guard.

Source

Thrown at lib/concurrent-ruby/concurrent/executor/immediate_executor.rb:27

  # received and no two operations can be performed simultaneously.
  #
  # This executor service exists mainly for testing an debugging. When used
  # it immediately runs every `#post` operation on the current thread, blocking
  # that thread until the operation is complete. This can be very beneficial
  # during testing because it makes all operations deterministic.
  #
  # @note Intended for use primarily in testing and debugging.
  class ImmediateExecutor < AbstractExecutorService
    include SerialExecutorService

    # Creates a new executor
    def initialize
      @stopped = Concurrent::Event.new
    end

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

    # @!macro executor_service_method_left_shift
    def <<(task)
      post(&task)
      self
    end

    # @!macro executor_service_method_running_question
    def running?
      ! shutdown?
    end

    # @!macro executor_service_method_shuttingdown_question
    def shuttingdown?

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Always call with a block: executor.post { do_work }
  2. Pass stored callables with &: executor.post(&task)
  3. Guard call sites with raise unless task.respond_to?(:call) before posting

Example fix

# before
executor.post
# after
executor.post { do_work }
Defensive patterns

Strategy: validation

Validate before calling

raise ArgumentError, 'task must respond to #call' unless task.respond_to?(:call)
executor.post(&task)

Try / catch

begin
  executor.post { do_work }
rescue ArgumentError => e
  raise unless e.message == 'no block given'
  raise ArgumentError, 'post requires a block'
end

Prevention

When it happens

Trigger: executor.post with no block; executor.post(1, 2) passing only args; passing a stored proc positionally instead of with &; executor << task where task is not convertible to a proc.

Common situations: Test doubles swapped in for real thread pools where the block was optional elsewhere; wrapper methods accepting an optional task; helpers that pass a method object without &.

Related errors


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