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 AbstractEventFutureView on GitHub (pinned to 0b88d5ff75)
Solutions
- Pass a level of at least 1; use bare flat (defaults to 1) for a single nesting layer.
- Clamp computed levels: f.flat([computed_level, 1].max).
- 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
- Treat 1 as the minimum flatten level; assert computed depths before calling flat.
- Use bare flat (default level 1) unless you knowingly have deeper nesting.
- Cover depth edge cases (0, negative) in tests for generic flattening helpers.
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
- ArgumentError
- no block given
- #{value} cannot be negative
- #{value} cannot be negative or zero
- executor not recognized by '#{executor_identifier}'
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/174cc1a873dc5700.
Report an issue: GitHub.