bblimke/webmock · error · RuntimeError

Invalid WebMock stub declaration. times(N) can be declared…

Error message

Invalid WebMock stub declaration. times(N) can be declared only after response declaration.

What it means

times() repeats the last response sequence (responses_sequences.last), so at least one response declaration (.to / .then) must already exist on the stub; otherwise RuntimeError 'Invalid WebMock stub declaration...' is raised. This is a DSL-ordering guard - before a .to call there is nothing to repeat.

Solutions

  1. Declare the response first, then repeat: stub_request(:get, 'www.example.com').to(body: 'ok').times(3)
  2. For multiple sequences chain .to(r1).then.to(r2).times(2) - times always applies to the last declared response
  3. If the stub needs no response yet, still declare .to(something) before times, or drop times entirely

Example fix

// before
stub = stub_request(:get, 'www.example.com')
stub.times(3)
# => RuntimeError: Invalid WebMock stub declaration...

// after
stub = stub_request(:get, 'www.example.com').to(body: 'ok')
stub.times(3)
Defensive patterns

Strategy: try-catch

Try / catch

begin
  stub.times(3)
rescue RuntimeError => e
  raise unless e.message.include?('declared only after response declaration')
  stub.to(body: 'ok').times(3) # attach a response first, then repeat
end

Prevention

When it happens

Trigger: stub_request(:get, 'www.example.com').times(3) with no .to anywhere; splitting a stub across lines and calling times before to: stub.times(2).to(body: 'x'); refactors or helper methods that attach responses conditionally after times.

Common situations: Copy-pasting DSL examples and dropping the .to line; builder helpers that call times before responses are attached; misunderstanding that times binds to the preceding response declaration only.

Related errors


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

Appendix: source

Thrown at lib/webmock/request_stub.rb:101

        @responses_sequences.shift if @responses_sequences.first.end?
        @responses_sequences.first.next_response
      else
        @responses_sequences[0].next_response
      end
    end

    def has_responses?
      !@responses_sequences.empty?
    end

    def then
      self
    end

    def times(number)
      raise "times(N) accepts integers >= 1 only" if !number.is_a?(Integer) || number < 1
      if @responses_sequences.empty?
        raise "Invalid WebMock stub declaration." +
          " times(N) can be declared only after response declaration."
      end
      @responses_sequences.last.times_to_repeat += number-1
      self
    end

    def matches?(request_signature)
      self.request_pattern.matches?(request_signature)
    end

    def to_s
      self.request_pattern.to_s
    end

    def self.from_request_signature(signature)
      stub = self.new(signature.method.to_sym, signature.uri.to_s)

      if signature.body.to_s != ''

View on GitHub (pinned to b187df8827)