ruby-concurrency/concurrent-ruby · error · ArgumentError

levels has to be higher than 0

Error message

levels has to be higher than 0

What it means

Future#flat_future (alias flat, promises.rb:1126) flattens nested futures up to `level` levels deep. FlatFuturePromise#initialize (promises.rb:1886) requires level >= 1 and raises ArgumentError 'levels has to be higher than 0' otherwise, because a flattening of depth zero is meaningless.

Source

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

          when AbstractEventFuture
            add_delayed_of value
            value.add_callback_notify_blocked self, nil
            countdown
          else
            resolve_with RESOLVED
          end
        end
        countdown
      end

    end

    class FlatFuturePromise < AbstractFlatPromise

      private

      def initialize(delayed, blockers_count, levels, default_executor)
        raise ArgumentError, 'levels has to be higher than 0' if levels < 1
        # flat promise may result to a future having delayed futures, therefore we have to have empty stack
        # to be able to add new delayed futures
        super delayed || LockFreeStack.new, 1 + levels, Future.new(self, default_executor)
      end

      def process_on_blocker_resolution(future, index)
        countdown = super(future, index)
        if countdown.nonzero?
          internal_state = future.internal_state

          unless internal_state.fulfilled?
            resolve_with internal_state
            return countdown
          end

          value = internal_state.value
          case value
          when AbstractEventFuture

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Pass a level of at least 1; use bare flat (defaults to 1) for a single nesting layer.
  2. Clamp computed levels: f.flat([computed_level, 1].max).
  3. Validate the depth in your own helper before delegating to flat_future.

Example fix

# before
f.flat(depth - 1) # depth == 1 -> flat(0) -> ArgumentError

# after
f.flat([depth - 1, 1].max)
Defensive patterns

Strategy: validation

Validate before calling

def flatten(future, level)
  future.flat([level.to_i, 1].max) # never pass a level below 1
end

Prevention

When it happens

Trigger: future.flat(0) or future.flat_future(0); a dynamically computed level reaching 0, e.g. levels = depth - 1 applied to a future nested one level; configuration defaulting a missing depth to 0 instead of 1.

Common situations: Generic flattening helpers where nesting depth is computed from data; off-by-one confusion about whether level counts layers inclusively; defaults from config files.

Related errors


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