bblimke/webmock · error · RuntimeError

The basic_auth option value should be an array which contain

Error message

The basic_auth option value should be an array which contains 2 strings: username and password

What it means

with(basic_auth: ...) expects credentials as an Array of two Strings, e.g. ['user', 'pass']. set_basic_auth_as_headers! deletes the option, validates it, and converts it into an Authorization header via WebMock::Util::Headers.basic_auth_header(basic_auth[0], basic_auth[1]). A non-Array value, or any non-String element (symbols, integers, nil), fails the check basic_auth.map { |e| e.is_a?(String) }.uniq != [true] in lib/webmock/request_pattern.rb:74 and raises this message. Note it is a plain raise, so the exception class is RuntimeError, not ArgumentError.

Source

Thrown at lib/webmock/request_pattern.rb:76

      options = WebMock::Util::HashKeysStringifier.stringify_keys!(options, deep: true)
      HashValidator.new(options).validate_keys('body', 'headers', 'query', 'basic_auth')
      set_basic_auth_as_headers!(options)
      @body_pattern = BodyPattern.new(options['body']) if options.has_key?('body')
      @headers_pattern = HeadersPattern.new(options['headers']) if options.has_key?('headers')
      @uri_pattern.add_query_params(options['query']) if options.has_key?('query')
    end

    def set_basic_auth_as_headers!(options)
      if basic_auth = options.delete('basic_auth')
        validate_basic_auth!(basic_auth)
        options['headers'] ||= {}
        options['headers']['Authorization'] = WebMock::Util::Headers.basic_auth_header(basic_auth[0],basic_auth[1])
      end
    end

    def validate_basic_auth!(basic_auth)
      if !basic_auth.is_a?(Array) || basic_auth.map{|e| e.is_a?(String)}.uniq != [true]
        raise "The basic_auth option value should be an array which contains 2 strings: username and password"
      end
    end

    def create_uri_pattern(uri)
      if uri.is_a?(Regexp)
        URIRegexpPattern.new(uri)
      elsif uri.is_a?(Addressable::Template)
        URIAddressablePattern.new(uri)
      elsif uri.respond_to?(:call)
        URICallablePattern.new(uri)
      elsif uri.is_a?(::URI::Generic)
        URIStringPattern.new(uri.to_s)
      elsif uri.respond_to?(:to_str)
        URIStringPattern.new(uri.to_str)
      else
        raise ArgumentError.new("URI should be a String, Regexp, Addressable::Template, a callable object, or respond to #to_str. Got: #{uri.class}")
      end
    end

View on GitHub (pinned to b187df8827)

Solutions

  1. Pass two strings: .with(basic_auth: ['user', 'pass'])
  2. Convert the value you have: 'user:pass'.split(':', 2), or map each element with to_s so every entry is a String
  3. Set the header directly instead of the option: .with(headers: { 'Authorization' => WebMock::Util::Headers.basic_auth_header('user', 'pass') }) - identical to the internal transformation
  4. Guard ENV-based credentials: .with(basic_auth: [ENV['API_USER'].to_s, ENV['API_PASS'].to_s])

Example fix

# before
stub_request(:get, 'https://api.example.com/private').with(basic_auth: 'user:pass')  # RuntimeError

# after
stub_request(:get, 'https://api.example.com/private').with(basic_auth: ['user', 'pass'])
Defensive patterns

Strategy: type-guard

Validate before calling

creds = 'user:pass'.split(':', 2)
stub.with(basic_auth: creds) if creds.length == 2 && creds.all? { |c| c.is_a?(String) }

Type guard

def valid_basic_auth?(v)
  v.is_a?(Array) && v.length == 2 && v.all? { |e| e.is_a?(String) }
end

Try / catch

begin
  pattern.with(basic_auth: creds)
rescue RuntimeError => e
  raise unless e.message.include?('basic_auth')
  pattern.with(headers: { 'Authorization' => WebMock::Util::Headers.basic_auth_header(*creds.map(&:to_s)) })
end

Prevention

When it happens

Trigger: .with(basic_auth: 'user:pass') - a colon-joined string copied from a URL like https://user:pass@example.com. .with(basic_auth: %i[user pass]) - symbols loaded from YAML/config. .with(basic_auth: { username: 'u', password: 'p' }) - hash form. A credentials array where one element is nil because an ENV var is unset, e.g. ['user', ENV['PASS']].

Common situations: Copying userinfo credentials out of a URL and pasting them verbatim; config files that symbolize keys and values; shared stub factories that take credentials from settings objects returning symbols or nils.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


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