ruby-concurrency/concurrent-ruby · error · ArgumentError
no block given
Error message
no block given
What it means
Channel#each is the enumeration loop that pops items from the channel until it is closed and drained. Unlike core Ruby enumerables, this implementation does not return a lazy Enumerator when called without a block - it raises ArgumentError immediately, because the block is the only way to consume each item.
Source
Thrown at lib/concurrent-ruby-edge/concurrent/channel.rb:194
(item = do_poll) == Concurrent::NULL ? nil : item
end
def poll!
item = do_poll
raise Error if item == Concurrent::NULL
item
end
def poll?
if (item = do_poll) == Concurrent::NULL
Concurrent::Maybe.nothing
else
Concurrent::Maybe.just(item)
end
end
def each
raise ArgumentError.new('no block given') unless block_given?
loop do
item, more = do_next
if item != Concurrent::NULL
yield(item)
elsif !more
break
end
end
end
class << self
def timer(seconds)
Channel.new(Buffer::Timer.new(seconds))
end
alias_method :after, :timer
def ticker(interval)
Channel.new(Buffer::Ticker.new(interval))View on GitHub (pinned to 0b88d5ff75)
Solutions
- Pass the consuming block: channel.each { |item| handle(item) }.
- If you need items as a collection, drain explicitly with take/pop in a loop (after closing the channel) instead of relying on Enumerator chaining.
- In wrappers that forward to each, check block_given? and raise a clearer error naming your API.
Example fix
# before
channel.each # raises: no block given
# after
channel.each { |item| handle(item) } Defensive patterns
Strategy: validation
Validate before calling
def drain(channel)
raise ArgumentError, 'a consumer block is required' unless block_given?
channel.each { |item| yield(item) }
end Prevention
- This each never returns an Enumerator - do not chain each.each_slice or each.lazy on a channel.
- Check block_given? early in wrappers so the error names your API, not the library's.
- Close the channel before draining it into a collection.
When it happens
Trigger: channel.each invoked with no block - a bare channel.each statement, or channel.each(&nil) through a helper whose block variable was never set.
Common situations: Refactoring each { ... } into chains like each_with_object or map (which call each without a consumer block of the right shape); passing a conditional block (cond && proc {...}) that evaluates to nil; expecting Hash#each-style Enumerator semantics such as channel.each.each_slice(10).
Related errors
- no block given
- unbuffered channels cannot have a capacity
- capacity must be at least 1 for this buffer type
- size must be greater than 0
- invalid action
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/8449e3de2a7704ca.
Report an issue: GitHub.