{"record":{"id":"b630ec7c863076b8","repo":"ruby-concurrency/concurrent-ruby","slug":"no-block-given-b630ec","errorCode":null,"errorMessage":"no block given","messagePattern":"no block given","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/executor/java_executor_service.rb","lineNumber":22,"sourceCode":"  require 'concurrent/errors'\n  require 'concurrent/executor/abstract_executor_service'\n\n  module Concurrent\n\n    # @!macro abstract_executor_service_public_api\n    # @!visibility private\n    class JavaExecutorService < AbstractExecutorService\n      java_import 'java.lang.Runnable'\n\n      FALLBACK_POLICY_CLASSES = {\n        abort:       java.util.concurrent.ThreadPoolExecutor::AbortPolicy,\n        discard:     java.util.concurrent.ThreadPoolExecutor::DiscardPolicy,\n        caller_runs: java.util.concurrent.ThreadPoolExecutor::CallerRunsPolicy\n      }.freeze\n      private_constant :FALLBACK_POLICY_CLASSES\n\n      def post(*args, &task)\n        raise ArgumentError.new('no block given') unless block_given?\n        return fallback_action(*args, &task).call unless running?\n        @executor.submit Job.new(args, task)\n        true\n      rescue Java::JavaUtilConcurrent::RejectedExecutionException\n        raise RejectedExecutionError\n      end\n\n      def wait_for_termination(timeout = nil)\n        if timeout.nil?\n          ok = @executor.awaitTermination(60, java.util.concurrent.TimeUnit::SECONDS) until ok\n          true\n        else\n          @executor.awaitTermination(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)\n        end\n      end\n\n      def shutdown\n        synchronize do","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb#L4-L40","documentation":"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.","triggerScenarios":"pool.post with no block on JRuby; passing a callable positionally instead of with &; a dispatcher that forwards *args but not &block.","commonSituations":"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.","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)"],"exampleFix":"# before\ndef enqueue(task)\n  pool.post(task)\nend\n# after\ndef enqueue(&task)\n  pool.post(&task)\nend","handlingStrategy":"validation","validationCode":"raise ArgumentError, 'task must respond to #call' unless task.respond_to?(:call)\npool.post(&task)","typeGuard":null,"tryCatchPattern":"begin\n  pool.post { do_work }\nrescue ArgumentError => e\n  raise unless e.message == 'no block given'\n  raise ArgumentError, 'post requires a block'\nend","preventionTips":["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"],"tags":["ruby","jruby","concurrency","executor","argument-error","missing-block"],"backgroundTag":"missing-block-argument","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}