ruby-concurrency/concurrent-ruby · error · ArgumentError
no block given
Error message
no block given
What it means
Concurrent::Maybe.from is a factory that runs a block (with optional args) in a protected way: if it returns, you get a Just(value); if it raises, you get a Nothing wrapping the exception. The block is the whole point, so calling it without one raises ArgumentError 'no block given'.
Source
Thrown at lib/concurrent-ruby/concurrent/maybe.rb:138
# Create a new `Maybe` using the given block.
#
# Runs the given block passing all function arguments to the block as block
# arguments. If the block runs to completion without raising an exception
# a new `Just` is created with the value set to the return value of the
# block. If the block raises an exception a new `Nothing` is created with
# the reason being set to the raised exception.
#
# @param [Array<Object>] args Zero or more arguments to pass to the block.
# @yield The block from which to create a new `Maybe`.
# @yieldparam [Array<Object>] args Zero or more block arguments passed as
# arguments to the function.
#
# @return [Maybe] The newly created object.
#
# @raise [ArgumentError] when no block given.
def self.from(*args)
raise ArgumentError.new('no block given') unless block_given?
begin
value = yield(*args)
return new(value, NONE)
rescue => ex
return new(NONE, ex)
end
end
# Create a new `Just` with the given value.
#
# @param [Object] value The value to set for the new `Maybe` object.
#
# @return [Maybe] The newly created object.
def self.just(value)
return new(value, NONE)
end
# Create a new `Nothing` with the given (optional) reason.View on GitHub (pinned to 0b88d5ff75)
Solutions
- To wrap an existing value, use Concurrent::Maybe.new(value) or Concurrent::Maybe(value) — not from
- To run protected code, always give from a block: Maybe.from { risky } — extra args are passed to the block: Maybe.from(a, b) { |x, y| ... }
- If the operation is a stored callable, pass it as a block: Maybe.from(&callable)
Example fix
# before
result = Concurrent::Maybe.from(42)
# after
result = Concurrent::Maybe.new(42) # wrap existing value
result = Concurrent::Maybe.from { 42 } # run protected block Defensive patterns
Strategy: validation
Validate before calling
raise ArgumentError, 'operation block required' unless block_given?
Concurrent::Maybe.from(*args) { |*a| yield(*a) }
# or, to wrap an existing value:
Concurrent::Maybe.new(value) Type guard
->(obj) { obj.respond_to?(:call) } # then Maybe.from(&obj) Try / catch
begin
Concurrent::Maybe.from { risky }
rescue ArgumentError => e
raise ArgumentError, 'Maybe.from needs a block' if /no block/.match?(e.message)
raise
end Prevention
- Use Maybe.new/Maybe() for wrapping values; reserve from for protected block execution
- Assert block_given? in helper methods that wrap Maybe.from
- Pass stored callables as blocks with &
When it happens
Trigger: Concurrent::Maybe.from(value) expecting Just(value) — wrong API; Maybe.from(*args) with the computation forgotten; storing the operation in a variable and passing it positionally.
Common situations: Confusing from with Maybe(value)/Maybe.new(value) which wrap an existing value; generic 'run op or capture error' helpers that take callables positionally; conditional code paths where the block is built only sometimes and ends up nil.
Related errors
AI-assisted analysis of ruby-concurrency/concurrent-ruby@0b88d5ff75 (2026-08-21).
Data as JSON: /api/errors/a7e0a40585844beb.
Report an issue: GitHub.