bblimke/webmock · error · ArgumentError

#with method invoked with no arguments. Either options hash

Error message

#with method invoked with no arguments. Either options hash or block must be specified. Created a block with do..end? Try creating it with curly braces {} instead.

What it means

RequestPattern#with must receive either a non-empty options hash (body, headers, query, basic_auth) or a block; with an empty hash and no block the call is ambiguous, so webmock raises ArgumentError immediately (lib/webmock/request_pattern.rb:29). The do..end hint is about Ruby block precedence: in a longer chain a do..end block can bind to a different method than intended, leaving with() with no block at all, while curly braces bind to the nearest method. An options hash built at runtime can also collapse to {} when every conditional entry is skipped.

Source

Thrown at lib/webmock/request_pattern.rb:29

      matcher.class.name =~ /R?Spec::Mocks::ArgumentMatchers::HashExcludingMatcher/
    end
  end

  class RequestPattern

    attr_reader :method_pattern, :uri_pattern, :body_pattern, :headers_pattern

    def initialize(method, uri, options = {})
      @method_pattern  = MethodPattern.new(method)
      @uri_pattern     = create_uri_pattern(uri)
      @body_pattern    = nil
      @headers_pattern = nil
      @with_block      = nil
      assign_options(options)
    end

    def with(options = {}, &block)
      raise ArgumentError.new('#with method invoked with no arguments. Either options hash or block must be specified. Created a block with do..end? Try creating it with curly braces {} instead.') if options.empty? && !block_given?
      assign_options(options)
      @with_block = block
      self
    end

    def matches?(request_signature)
      content_type = request_signature.headers['Content-Type'] if request_signature.headers
      content_type = content_type.split(';').first if content_type
      @method_pattern.matches?(request_signature.method) &&
        @uri_pattern.matches?(request_signature.uri) &&
        (@body_pattern.nil? || @body_pattern.matches?(request_signature.body, content_type || "")) &&
        (@headers_pattern.nil? || @headers_pattern.matches?(request_signature.headers)) &&
        (@with_block.nil? || @with_block.call(request_signature))
    end

    def to_s
      string = "#{@method_pattern.to_s.upcase}".dup
      string << " #{@uri_pattern.to_s}"

View on GitHub (pinned to b187df8827)

Solutions

  1. Give with at least one real constraint: .with(body: {...}), .with(headers: {...}) or .with(query: {...})
  2. Or express the constraint as a block with curly braces: .with { |req| req.body.include?('token') }
  3. If no narrowing is needed, remove the .with call entirely - stub_request(:get, url) already matches method and URI
  4. For dynamic options, only chain with when the hash is non-empty: stub = a_request(:get, url); stub = stub.with(opts) unless opts.empty?

Example fix

# before
opts = {}
opts[:query] = { page: 2 } if paginated
stub_request(:get, 'https://api.example.com/items').with(opts)  # ArgumentError when paginated is false

# after
opts = {}
opts[:query] = { page: 2 } if paginated
stub = stub_request(:get, 'https://api.example.com/items')
stub = stub.with(opts) unless opts.empty?
Defensive patterns

Strategy: validation

Validate before calling

# Helper that never calls .with with empty options:
def apply_with(stub, opts = {}, &blk)
  opts.empty? && blk.nil? ? stub : stub.with(opts, &blk)
end

stub = apply_with(stub_request(:get, url), conditional_opts)

Prevention

When it happens

Trigger: Calling .with({}) or .with() with no arguments. Building options dynamically (opts = {}; opts[:body] = b if condition; stub_request(:get, url).with(opts)) when the condition is false so opts stays empty. Attaching the constraint block with do..end to the wrong method of a chain so with() receives neither options nor a block. Shared examples that forward an optional with: hash that defaults to {}

Common situations: Shared stub helpers that accept an optional options hash and forward it blindly; refactoring that moves a body/headers constraint into a conditional branch; copy-pasting README block examples but using do..end inside a bigger method chain; a typo like .with(body:) where the value on the next line is an empty hash.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of bblimke/webmock@b187df8827 (2026-08-23). Data as JSON: /api/errors/f955bcfc6a1e1126. Report an issue: GitHub.