ruby-concurrency/concurrent-ruby · error · ArgumentError
no block given
Error message
no block given
What it means
IndirectImmediateExecutor is an ImmediateExecutor that runs the task on an internal SimpleExecutorService thread and blocks the caller on an Event until it finishes, avoiding stack growth from nested immediate execution. post(*args, &task) raises ArgumentError('no block given') when the block is missing.
Source
Thrown at lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb:28
# immediately runs every `#post` operation on a new thread, blocking the
# current thread until the operation is complete. This is similar to how the
# ImmediateExecutor works, but the operation has the full stack of the new
# thread at its disposal. This can be helpful when the operations will spawn
# more operations on the same executor and so on - such a situation might
# overflow the single stack in case of an ImmediateExecutor, which is
# inconsistent with how it would behave for a threaded executor.
#
# @note Intended for use primarily in testing and debugging.
class IndirectImmediateExecutor < ImmediateExecutor
# Creates a new executor
def initialize
super
@internal_executor = SimpleExecutorService.new
end
# @!macro executor_service_method_post
def post(*args, &task)
raise ArgumentError.new("no block given") unless block_given?
return false unless running?
event = Concurrent::Event.new
@internal_executor.post do
begin
task.call(*args)
ensure
event.set
end
end
event.wait
true
end
end
end
View on GitHub (pinned to 0b88d5ff75)
Solutions
- Always call with a block: executor.post { do_work }
- Pass stored callables with &: executor.post(&task)
- Check task.respond_to?(:call) before posting in generic dispatch code
Example fix
# before
executor.post(:arg)
# after
executor.post(:arg) { |a| do_work(a) } 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(:arg) { |a| run(a) }
rescue ArgumentError => e
raise unless e.message == 'no block given'
raise ArgumentError, 'post requires a block'
end Prevention
- Pass blocks, not positional procs, when posting
- Keep executor-swapping test layers block-transparent (forward &block)
- Validate callables at the boundary of generic dispatch code
When it happens
Trigger: executor.post with no block; passing args only (executor.post(:job)); forwarding a stored proc without the & sigil.
Common situations: Testing harnesses where a callable is optional and nil slips through; refactoring an executor-swapping layer (tests use IndirectImmediateExecutor, production uses a real pool) that drops the block on one path.
Related errors
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/79c0a35c7bd28fe8.
Report an issue: GitHub.