ruby-concurrency/concurrent-ruby · error · ArgumentError

no block given

Error message

no block given

What it means

Concurrent::ReadWriteLock#with_read_lock executes its block while holding the shared read lock and releases it in an ensure clause. The block is the entire critical section, so the method raises ArgumentError immediately when no block is attached.

Source

Thrown at lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb:78

    def initialize
      super()
      @Counter   = AtomicFixnum.new(0)      # single integer which represents lock state
      @Writer    = AtomicReference.new(nil) # the thread currently holding the write lock
      @ReadLock  = Synchronization::Lock.new
      @WriteLock = Synchronization::Lock.new
    end

    # Execute a block operation within a read lock.
    #
    # @yield the task to be performed within the lock.
    #
    # @return [Object] the result of the block operation.
    #
    # @raise [ArgumentError] when no block is given.
    # @raise [Concurrent::ResourceLimitError] if the maximum number of readers
    #   is exceeded.
    def with_read_lock
      raise ArgumentError.new('no block given') unless block_given?
      acquire_read_lock
      begin
        yield
      ensure
        release_read_lock
      end
    end

    # Execute a block operation within a write lock.
    #
    # @yield the task to be performed within the lock.
    #
    # @return [Object] the result of the block operation.
    #
    # @raise [ArgumentError] when no block is given.
    # @raise [Concurrent::ResourceLimitError] if the maximum number of readers
    #   is exceeded.
    def with_write_lock

View on GitHub (pinned to 0b88d5ff75)

Solutions

  1. Pass the read critical section as a block: `lock.with_read_lock { data.retrieve }`.
  2. If you cannot use a block, use the manual pair acquire_read_lock / release_read_lock wrapped in begin/ensure.
  3. When wrapping in a helper, forward the block explicitly: `def read(&blk) lock.with_read_lock(&blk) end`.

Example fix

// before
lock.with_read_lock
data.retrieve

// after
lock.with_read_lock { data.retrieve }

// or manual pairing
lock.acquire_read_lock
begin
  data.retrieve
ensure
  lock.release_read_lock
end
Defensive patterns

Strategy: validation

Validate before calling

raise ArgumentError, 'read section block required' unless block_given?
lock.with_read_lock { yield }

Try / catch

begin
  lock.with_read_lock { read_all }
rescue ArgumentError => e
  raise unless e.message == 'no block given'
end

Prevention

When it happens

Trigger: `lock.with_read_lock` with no block; `lock.with_read_lock(&nil)`; helper methods that wrap with_read_lock but forget to forward `yield`/`&blk`.

Common situations: Intending to hold the lock manually across statements and reaching for the block API by mistake (use acquire_read_lock/release_read_lock instead); refactors that moved the block into a conditional branch; builders or DSLs that drop blocks silently.

Related errors


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