ruby-concurrency/concurrent-ruby · error · ArgumentError
no block given
Error message
no block given
What it means
On JRuby, thread pools (ThreadPoolExecutor, FixedThreadPool, SingleThreadExecutor, CachedThreadPool) are backed by JavaExecutorService, whose post wraps the block in a Job and submits it to a java.util.concurrent executor. It raises ArgumentError('no block given') when post is called without a block.
Source
Thrown at lib/concurrent-ruby/concurrent/executor/java_executor_service.rb:22
require 'concurrent/errors'
require 'concurrent/executor/abstract_executor_service'
module Concurrent
# @!macro abstract_executor_service_public_api
# @!visibility private
class JavaExecutorService < AbstractExecutorService
java_import 'java.lang.Runnable'
FALLBACK_POLICY_CLASSES = {
abort: java.util.concurrent.ThreadPoolExecutor::AbortPolicy,
discard: java.util.concurrent.ThreadPoolExecutor::DiscardPolicy,
caller_runs: java.util.concurrent.ThreadPoolExecutor::CallerRunsPolicy
}.freeze
private_constant :FALLBACK_POLICY_CLASSES
def post(*args, &task)
raise ArgumentError.new('no block given') unless block_given?
return fallback_action(*args, &task).call unless running?
@executor.submit Job.new(args, task)
true
rescue Java::JavaUtilConcurrent::RejectedExecutionException
raise RejectedExecutionError
end
def wait_for_termination(timeout = nil)
if timeout.nil?
ok = @executor.awaitTermination(60, java.util.concurrent.TimeUnit::SECONDS) until ok
true
else
@executor.awaitTermination(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)
end
end
def shutdown
synchronize doView on GitHub (pinned to 0b88d5ff75)
Solutions
- Always call with a block: pool.post { do_work }
- Forward blocks explicitly when wrapping executors: 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 wrapper methods as def enqueue(&task); pool.post(&task); end
- Never forward *args without &block in executor abstractions
- Run the JRuby suite too: the same bug surfaces with a different backtrace per interpreter
When it happens
Trigger: pool.post with no block on JRuby; passing a callable positionally instead of with &; a dispatcher that forwards *args but not &block.
Common situations: Same missing-block bugs as MRI, but the backtrace points at java_executor_service.rb because concurrent-ruby selects the implementation per interpreter; teams debugging on MRI cannot reproduce the identical stack on JRuby and vice versa.
Related errors
- no block given
- no block given
- #{@fallback_policy} is not a valid fallback policy
- no block given
- no block given
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/b630ec7c863076b8.
Report an issue: GitHub.