bblimke/webmock · error · ArgumentError

#to_return_json does not support passing a block

Error message

#to_return_json does not support passing a block

What it means

RequestStub#to_return_json builds JSON responses from one or more response hashes: it flattens the arguments, serializes bodies, and sets the JSON content type. Unlike to_return, it accepts no block - to_return_json(*response_hashes) raises ArgumentError immediately when a block is attached (lib/webmock/request_stub.rb:30). Dynamic JSON responses are still supported: a lambda passed as the :body value is called with the request signature and its return value is serialized to JSON.

Source

Thrown at lib/webmock/request_stub.rb:30

    end

    def with(params = {}, &block)
      @request_pattern.with(params, &block)
      self
    end

    def to_return(*response_hashes, &block)
      if block
        @responses_sequences << ResponsesSequence.new([ResponseFactory.response_for(block)])
      else
        @responses_sequences << ResponsesSequence.new([*response_hashes].flatten.map {|r| ResponseFactory.response_for(r)})
      end
      self
    end
    alias_method :and_return, :to_return

    def to_return_json(*response_hashes)
      raise ArgumentError, '#to_return_json does not support passing a block' if block_given?

      json_response_hashes = [*response_hashes].flatten.map do |resp_h|
        headers, body = resp_h.values_at(:headers, :body)

        json_body = if body.respond_to?(:call)
          ->(request_signature) {
            b = if body.respond_to?(:arity) && body.arity == 1
              body.call(request_signature)
            else
              body.call
            end
            b = b.to_json unless b.is_a?(String)
            b
          }
        elsif !body.is_a?(String)
          body.to_json
        else
          body

View on GitHub (pinned to b187df8827)

Solutions

  1. Remove the block and put the payload in the hash: .to_return_json(status: 200, body: { id: 1 })
  2. For dynamic JSON, pass a lambda as the :body value: .to_return_json(status: 200, body: ->(request) { { id: request.body[/\d+/].to_i } })
  3. If status and headers must also vary per request, use the classic form: .to_return(lambda { |request_signature| { status: 200, body: { id: 1 }.to_json } })

Example fix

# before
stub.to_return_json(status: 200) { |request| { id: request.body[/\d+/].to_i } }  # ArgumentError

# after
stub.to_return_json(status: 200, body: ->(request) { { id: request.body[/\d+/].to_i } })
Defensive patterns

Strategy: validation

Validate before calling

# Helper that routes dynamic JSON through :body instead of a block:
def stub_json(stub, dynamic: nil, **opts)
  dynamic ? stub.to_return_json(body: dynamic, **opts) : stub.to_return_json(**opts)
end

Prevention

When it happens

Trigger: .to_return_json(status: 200) { ... } - a block, typically left over from copy-pasting a to_return { |request| ... } dynamic stub. Using the and_return_json alias with a block. A spec helper that captures and blindly forwards &block to to_return_json.

Common situations: Migrating string-body dynamic stubs to to_return_json and keeping the block; helper methods that always forward a block parameter; examples written against a different stubbing API.

Related errors


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