{"record":{"id":"7f07b6fe472e749f","repo":"ruby-concurrency/concurrent-ruby","slug":"no-block-given-7f07b6","errorCode":null,"errorMessage":"no block given","messagePattern":"no block given","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb","lineNumber":18,"sourceCode":"require 'concurrent/executor/abstract_executor_service'\nrequire 'concurrent/atomic/event'\n\nmodule Concurrent\n\n  # @!macro abstract_executor_service_public_api\n  # @!visibility private\n  class RubyExecutorService < AbstractExecutorService\n    safe_initialization!\n\n    def initialize(*args, &block)\n      super\n      @StopEvent    = Event.new\n      @StoppedEvent = Event.new\n    end\n\n    def post(*args, &task)\n      raise ArgumentError.new('no block given') unless block_given?\n      deferred_action = synchronize {\n        if running?\n          ns_execute(*args, &task)\n        else\n          fallback_action(*args, &task)\n        end\n      }\n      if deferred_action\n        deferred_action.call\n      else\n        true\n      end\n    end\n\n    def shutdown\n      synchronize do\n        break unless running?\n        stop_event.set","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb#L1-L36","documentation":"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.","triggerScenarios":"pool.post with no block on MRI; executor.post(arg) passing only arguments; a generic dispatcher forwarding *args but not &block; passing a proc positionally.","commonSituations":"Wrapper/gem code that enqueues optional tasks; refactoring a call chain and losing the block; test stubs replacing executors where the block was optional.","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)"],"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 wrappers as def enqueue(&task); pool.post(&task); end","Pass variable callables with &","Lint for post( without a block or &arg in review"],"tags":["ruby","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"}