ruby-concurrency/concurrent-ruby · error · ArgumentError
no block given
Error message
no block given
What it means
On MRI, thread pools (RubyThreadPoolExecutor and friends) are backed by RubyExecutorService, whose post schedules the block via ns_execute or routes it to the fallback action when not running. It raises ArgumentError('no block given') when post is called without a block, before any state check.
Source
Thrown at lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb:18
require 'concurrent/executor/abstract_executor_service'
require 'concurrent/atomic/event'
module Concurrent
# @!macro abstract_executor_service_public_api
# @!visibility private
class RubyExecutorService < AbstractExecutorService
safe_initialization!
def initialize(*args, &block)
super
@StopEvent = Event.new
@StoppedEvent = Event.new
end
def post(*args, &task)
raise ArgumentError.new('no block given') unless block_given?
deferred_action = synchronize {
if running?
ns_execute(*args, &task)
else
fallback_action(*args, &task)
end
}
if deferred_action
deferred_action.call
else
true
end
end
def shutdown
synchronize do
break unless running?
stop_event.setView on GitHub (pinned to 0b88d5ff75)
Solutions
- Always call with a block: pool.post { do_work }
- Forward blocks explicitly when wrapping: def enqueue(&task); pool.post(&task); end
- Pass stored callables with &: pool.post(&task)
Example fix
# before def enqueue(task) pool.post(task) end # after def enqueue(&task) pool.post(&task) end
Defensive patterns
Strategy: validation
Validate before calling
raise ArgumentError, 'task must respond to #call' unless task.respond_to?(:call) pool.post(&task)
Try / catch
begin
pool.post { do_work }
rescue ArgumentError => e
raise unless e.message == 'no block given'
raise ArgumentError, 'post requires a block'
end Prevention
- Write wrappers as def enqueue(&task); pool.post(&task); end
- Pass variable callables with &
- Lint for post( without a block or &arg in review
When it happens
Trigger: pool.post with no block on MRI; executor.post(arg) passing only arguments; a generic dispatcher forwarding *args but not █ passing a proc positionally.
Common situations: Wrapper/gem code that enqueues optional tasks; refactoring a call chain and losing the block; test stubs replacing executors where the block was optional.
Related errors
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/7f07b6fe472e749f.
Report an issue: GitHub.