{"record":{"id":"1c6bc1a6c0082235","repo":"ruby-concurrency/concurrent-ruby","slug":"no-block-given-1c6bc1","errorCode":null,"errorMessage":"no block given","messagePattern":"no block given","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/concurrent-ruby/concurrent/executor/timer_set.rb","lineNumber":49,"sourceCode":"      super(opts)\n    end\n\n    # Post a task to be execute run after a given delay (in seconds). If the\n    # delay is less than 1/100th of a second the task will be immediately post\n    # to the executor.\n    #\n    # @param [Float] delay the number of seconds to wait for before executing the task.\n    # @param [Array<Object>] args the arguments passed to the task on execution.\n    #\n    # @yield the task to be performed.\n    #\n    # @return [Concurrent::ScheduledTask, false] IVar representing the task if the post\n    #   is successful; false after shutdown.\n    #\n    # @raise [ArgumentError] if the intended execution time is not in the future.\n    # @raise [ArgumentError] if no block is given.\n    def post(delay, *args, &task)\n      raise ArgumentError.new('no block given') unless block_given?\n      return false unless running?\n      opts = { executor:  @task_executor,\n               args:      args,\n               timer_set: self }\n      task = ScheduledTask.execute(delay, opts, &task) # may raise exception\n      task.unscheduled? ? false : task\n    end\n\n    # Begin an immediate shutdown. In-progress tasks will be allowed to\n    # complete but enqueued tasks will be dismissed and no new tasks\n    # will be accepted. Has no additional effect if the thread pool is\n    # not running.\n    def kill\n      shutdown\n      @timer_executor.kill\n    end\n\n    private :<<","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/ruby-concurrency/concurrent-ruby/blob/0b88d5ff75f69b3740c8f0868e76f833cb2fd45d/lib/concurrent-ruby/concurrent/executor/timer_set.rb#L31-L67","documentation":"TimerSet is the internal executor behind Concurrent::timer and ScheduledTask scheduling. Its #post(delay, *args) takes the delay as the first argument and the task as a block; calling it without a block raises ArgumentError 'no block given'. The delay itself is validated separately by ScheduledTask.execute, which requires a future execution time.","triggerScenarios":"timer_set.post(2) with no block; passing the task as a positional argument after the delay: timer_set.post(2, task); forgetting that the first parameter is the delay and leading with the block's args instead.","commonSituations":"Code reaching into TimerSet directly instead of using the public Concurrent::timer API; porting timer code from other libs where the callable is positional; wrapping timers in adapter classes that drop the &block forwarding.","solutions":["Use the public API: Concurrent::timer(2) { do_work } instead of touching TimerSet","Pass the task as a block after the delay: timer_set.post(2) { do_work }","If the callable is stored, pass it as a block: timer_set.post(2, &callable)"],"exampleFix":"# before\ntimer_set.post(2)\n\n# after\nConcurrent::timer(2) { do_work }","handlingStrategy":"validation","validationCode":"raise ArgumentError, 'task block required' unless block_given?\nConcurrent::timer(delay) { yield } # prefer the public API over TimerSet","typeGuard":"->(d) { d.is_a?(Numeric) && d >= 0 } # delay guard, not a block guard","tryCatchPattern":"begin\n  timer_set.post(delay, &task)\nrescue ArgumentError => e\n  raise ArgumentError, \"timer post rejected: #{e.message}\" unless /no block/.match?(e.message)\n  raise\nend","preventionTips":["Use Concurrent::timer / Concurrent::scheduled_task instead of touching TimerSet internals","Assert block_given? in timer wrappers before calling post","Remember post's signature: post(delay, *args) { |*args| ... }"],"tags":["ruby","concurrency","scheduler","timer","block-argument","argument-validation"],"backgroundTag":"missing-block-argument","analyzedSha":"0b88d5ff75f69b3740c8f0868e76f833cb2fd45d","analyzedAt":"2026-08-21T20:12:56.291Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}