ruby-concurrency/concurrent-ruby · error · ArgumentError

provide only a value or a block

Error message

provide only a value or a block

What it means

An IVar is a write-once container that starts pending and is completed exactly once. Its constructor accepts either an initial value or a block that computes the value — never both. Passing a value and a block simultaneously raises ArgumentError 'provide only a value or a block'.

Source

Thrown at lib/concurrent-ruby/concurrent/ivar.rb:64

  # 2. For recent application:
  #    [DataDrivenFuture in Habanero Java from Rice](http://www.cs.rice.edu/~vs3/hjlib/doc/edu/rice/hj/api/HjDataDrivenFuture.html).
  class IVar < Synchronization::LockableObject
    include Concern::Obligation
    include Concern::Observable

    # Create a new `IVar` in the `:pending` state with the (optional) initial value.
    #
    # @param [Object] value the initial value
    # @param [Hash] opts the options to create a message with
    # @option opts [String] :dup_on_deref (false) call `#dup` before returning
    #   the data
    # @option opts [String] :freeze_on_deref (false) call `#freeze` before
    #   returning the data
    # @option opts [String] :copy_on_deref (nil) call the given `Proc` passing
    #   the internal value and returning the value returned from the proc
    def initialize(value = NULL, opts = {}, &block)
      if value != NULL && block_given?
        raise ArgumentError.new('provide only a value or a block')
      end
      super(&nil)
      synchronize { ns_initialize(value, opts, &block) }
    end

    # Add an observer on this object that will receive notification on update.
    #
    # Upon completion the `IVar` will notify all observers in a thread-safe way.
    # The `func` method of the observer will be called with three arguments: the
    # `Time` at which the `Future` completed the asynchronous operation, the
    # final `value` (or `nil` on rejection), and the final `reason` (or `nil` on
    # fulfillment).
    #
    # @param [Object] observer the object that will be notified of changes
    # @param [Symbol] func symbol naming the method to call when this
    #   `Observable` has changes`
    def add_observer(observer = nil, func = :update, &block)
      raise ArgumentError.new('cannot provide both an observer and a block') if observer && block

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Choose one: Concurrent::IVar.new(42) for a known value, or Concurrent::IVar.new { compute } for a deferred one
  2. If you need options, put them in the hash after the value/default: Concurrent::IVar.new { compute } is equivalent to passing opts in many examples — keep the value slot untouched
  3. If both a value and computation exist, decide precedence in your own code and pass only the winner

Example fix

# before
ivar = Concurrent::IVar.new(42) { expensive_default }

# after
ivar = Concurrent::IVar.new(42)
# or, when the value must be computed:
ivar = Concurrent::IVar.new { expensive_default }
Defensive patterns

Strategy: validation

Validate before calling

def build_ivar(value = nil, has_value: false, &block)
  raise ArgumentError, 'value or block, not both' if has_value && block
  has_value ? Concurrent::IVar.new(value) : Concurrent::IVar.new(&block)
end

Try / catch

begin
  Concurrent::IVar.new(value) { compute }
rescue ArgumentError => e
  raise ArgumentError, 'pick value or block for IVar' if /value or a block/.match?(e.message)
  raise
end

Prevention

When it happens

Trigger: Concurrent::IVar.new(42) { compute } — value plus block; copying an initialization pattern from IVar#set (which also takes value-or-block) into the constructor while keeping both; passing a value that was meant to be an options hash entry.

Common situations: Refactors where a computed default was replaced by a literal but the block stayed; mixing :executor-style option hashes with the positional value slot; subclasses calling super with both.

Related errors


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