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
- Always call with a block: executor.post { do_work }
- Pass stored callables with &: executor.post(&task)
- 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
- Always supply a literal block to post
- Forward blocks with & in executor wrappers
- In test harnesses using ImmediateExecutor, assert the callable is present before posting
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.