{"record":{"id":"cc86e297d2ef6aa0","repo":"ruby-concurrency/concurrent-ruby","slug":"no-block-given-cc86e2","errorCode":null,"errorMessage":"no block given","messagePattern":"no block given","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/executor/immediate_executor.rb","lineNumber":27,"sourceCode":"  # received and no two operations can be performed simultaneously.\n  #\n  # This executor service exists mainly for testing an debugging. When used\n  # it immediately runs every `#post` operation on the current thread, blocking\n  # that thread until the operation is complete. This can be very beneficial\n  # during testing because it makes all operations deterministic.\n  #\n  # @note Intended for use primarily in testing and debugging.\n  class ImmediateExecutor < AbstractExecutorService\n    include SerialExecutorService\n\n    # Creates a new executor\n    def initialize\n      @stopped = Concurrent::Event.new\n    end\n\n    # @!macro executor_service_method_post\n    def post(*args, &task)\n      raise ArgumentError.new('no block given') unless block_given?\n      return false unless running?\n      task.call(*args)\n      true\n    end\n\n    # @!macro executor_service_method_left_shift\n    def <<(task)\n      post(&task)\n      self\n    end\n\n    # @!macro executor_service_method_running_question\n    def running?\n      ! shutdown?\n    end\n\n    # @!macro executor_service_method_shuttingdown_question\n    def shuttingdown?","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb#L9-L45","documentation":"ImmediateExecutor runs tasks synchronously on the caller's thread and exists mainly for testing and debugging. Its post(*args, &task) raises ArgumentError('no block given') when no block is supplied; the check runs before the running? guard.","triggerScenarios":"executor.post with no block; executor.post(1, 2) passing only args; passing a stored proc positionally instead of with &; executor << task where task is not convertible to a proc.","commonSituations":"Test doubles swapped in for real thread pools where the block was optional elsewhere; wrapper methods accepting an optional task; helpers that pass a method object without &.","solutions":["Always call with a block: executor.post { do_work }","Pass stored callables with &: executor.post(&task)","Guard call sites with raise unless task.respond_to?(:call) before posting"],"exampleFix":"# before\nexecutor.post\n# after\nexecutor.post { do_work }","handlingStrategy":"validation","validationCode":"raise ArgumentError, 'task must respond to #call' unless task.respond_to?(:call)\nexecutor.post(&task)","typeGuard":null,"tryCatchPattern":"begin\n  executor.post { do_work }\nrescue ArgumentError => e\n  raise unless e.message == 'no block given'\n  raise ArgumentError, 'post requires a block'\nend","preventionTips":["Always supply a literal block to post","Forward blocks with & in executor wrappers","In test harnesses using ImmediateExecutor, assert the callable is present before posting"],"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"}