ruby-concurrency/concurrent-ruby · error · ArgumentError

no block given

Error message

no block given

What it means

BlockedTaskPromise (promises.rb:1723) is the base class of the task-carrying chain steps — ThenPromise, RescuePromise, EnsurePromise, created by Future#then, #rescue, and #ensure. Each requires a block; building the chain step without one raises ArgumentError 'no block given' immediately at chain-construction time, before anything executes.

Source

Thrown at lib/concurrent-ruby/concurrent/promises.rb:1724

      # @return [true,false] if resolvable
      def resolvable?(countdown, future, index)
        countdown.zero?
      end

      def process_on_blocker_resolution(future, index)
        @Countdown.decrement
      end

      def on_resolvable(resolved_future, index)
        raise NotImplementedError
      end
    end

    # @abstract
    class BlockedTaskPromise < BlockedPromise
      def initialize(delayed, blockers_count, default_executor, executor, args, &task)
        raise ArgumentError, 'no block given' unless block_given?
        super delayed, 1, Future.new(self, default_executor)
        @Executor = executor
        @Task     = task
        @Args     = args
      end

      def executor
        @Executor
      end
    end

    class ThenPromise < BlockedTaskPromise
      private

      def initialize(delayed, blockers_count, default_executor, executor, args, &task)
        super delayed, blockers_count, default_executor, executor, args, &task
      end

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Supply the transformation block: f.then { |v| ... }.
  2. For an intentional no-op pass-through use f.then { |v| v }.
  3. If you only want to trigger/await execution, use f.wait, f.value, or f.run instead of then.

Example fix

# before
f2 = f.then # ArgumentError: no block given

# after
f2 = f.then { |v| v } # explicit pass-through
Defensive patterns

Strategy: validation

Validate before calling

def then_or_noop(future, &block)
  block ? future.then(&block) : future.then { |v| v }
end

Prevention

When it happens

Trigger: future.then with no block (missing { }); future.rescue without a block when the author only wanted failure propagation; conditional chaining like cond ? f.then { |v| v } : f.then where one branch lost its block.

Common situations: Refactors that delete or move block bodies; intending a no-op pass-through; assuming then merely schedules execution of the upstream future.

Related errors


AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21). Data as JSON: /api/errors/107613e0487e8332. Report an issue: GitHub.