ruby-concurrency/concurrent-ruby · error · ArgumentError

no block given

Error message

no block given

What it means

Concurrent::ReentrantReadWriteLock#with_read_lock executes its block while the current thread holds the (reentrant) shared read lock, releasing it in an ensure clause. As with the plain ReadWriteLock, the block API exists to run a block, so it raises ArgumentError immediately when no block is attached.

Source

Thrown at lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb:127

    def initialize
      super()
      @Counter    = AtomicFixnum.new(0)       # single integer which represents lock state
      @ReadQueue  = Synchronization::Lock.new # used to queue waiting readers
      @WriteQueue = Synchronization::Lock.new # used to queue waiting writers
      @HeldCount  = LockLocalVar.new(0) # indicates # of R & W locks held by this thread
    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 { read_all }`.
  2. For scopeless holding use acquire_read_lock / release_read_lock in begin/ensure (the reentrant counter makes same-thread reacquire safe).
  3. Forward blocks with &blk in wrappers.

Example fix

// before
lock.with_read_lock
rows = read_all

// after
lock.with_read_lock { read_all }
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; `rwl.with_read_lock(&nil)`; wrapper methods around with_read_lock that fail to forward the block.

Common situations: Migrating code from acquire/release style to block style and leaving a call site bare; wrappers or decorators that accept no block yet call with_read_lock.

Related errors


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