Skip to content

undefined method `strip' for an instance of Hash #301

@swombat

Description

@swombat

After much, much, much debugging, while getting the mysterious error undefined method strip' for an instance of Hash` (btw, whoever decided to catch that error and print it out and carry on without providing the stack trace... I hope you take a good long look at yourself in the mirror some day)...

I ended up diagnosing it as an issue with a parameter has being passed into net/http.

The full stack trace:

undefined method `strip' for an instance of Hash
======== /Users/danieltenner/.rvm/rubies/ruby-3.3.1/lib/ruby/3.3.0/net/http/header.rb:194:in `block in initialize_http_header'
/Users/danieltenner/.rvm/rubies/ruby-3.3.1/lib/ruby/3.3.0/net/http/header.rb:189:in `each'
/Users/danieltenner/.rvm/rubies/ruby-3.3.1/lib/ruby/3.3.0/net/http/header.rb:189:in `initialize_http_header'
/Users/danieltenner/.rvm/rubies/ruby-3.3.1/lib/ruby/3.3.0/net/http/generic_request.rb:51:in `initialize'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/faraday-net_http-3.1.1/lib/faraday/adapter/net_http.rb:79:in `new'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/faraday-net_http-3.1.1/lib/faraday/adapter/net_http.rb:79:in `create_request'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/faraday-net_http-3.1.1/lib/faraday/adapter/net_http.rb:112:in `block in request_with_wrapped_block'
/Users/danieltenner/.rvm/rubies/ruby-3.3.1/lib/ruby/3.3.0/net/http.rb:1570:in `start'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/faraday-net_http-3.1.1/lib/faraday/adapter/net_http.rb:111:in `request_with_wrapped_block'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/faraday-net_http-3.1.1/lib/faraday/adapter/net_http.rb:101:in `perform_request'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/faraday-net_http-3.1.1/lib/faraday/adapter/net_http.rb:65:in `block in call'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/faraday-2.10.1/lib/faraday/adapter.rb:45:in `connection'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/faraday-net_http-3.1.1/lib/faraday/adapter/net_http.rb:64:in `call'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/faraday-2.10.1/lib/faraday/middleware.rb:56:in `call'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/faraday-2.10.1/lib/faraday/rack_builder.rb:152:in `build_response'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/faraday-2.10.1/lib/faraday/connection.rb:444:in `run_request'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/faraday-2.10.1/lib/faraday/connection.rb:200:in `get'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/xero-ruby-9.2.0/lib/xero-ruby/api_client.rb:319:in `public_send'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/xero-ruby-9.2.0/lib/xero-ruby/api_client.rb:319:in `call_api'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/xero-ruby-9.2.0/lib/xero-ruby/api/accounting_api.rb:6339:in `get_accounts_with_http_info'
/Users/danieltenner/.rvm/gems/ruby-3.3.1/gems/xero-ruby-9.2.0/lib/xero-ruby/api/accounting_api.rb:6275:in `get_accounts'
/Users/danieltenner/dev/[...]/app/apis/xero_api.rb:60:in `get_accounts'

This is happening because the initheader parameter passed to net/http/header.rb's initialize_http_header method is:

{"Content-Type"=>"application/json", "User-Agent"=>"xero-ruby-9.2.0", 
"Accept"=>"application/json", "Xero-tenant-id"=>{"id"=>"[snip]", "authEventId"=>"[snip]", 
"tenantId"=>"[snip]", "tenantType"=>"ORGANISATION", "tenantName"=>"[snip]", 
"createdDateUtc"=>"2024-09-04T15:17:17.9469550", "updatedDateUtc"=>"2024-09-04T15:17:17.9483740"}, "Authorization"=>"Bearer [snip]", 
"accept-encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3"}

Notice Xero-tenant-id is a hash.

initialize_http_header then dutifully chokes on it because it expects string parameters:

  def initialize_http_header(initheader) #:nodoc:
    @header = {}
    return unless initheader
    initheader.each do |key, value|
      warn "net/http: duplicated HTTP header: #{key}", uplevel: 3 if key?(key) and $VERBOSE
      if value.nil?
        warn "net/http: nil HTTP header: #{key}", uplevel: 3 if $VERBOSE
      else
        value = value.strip # raise error for invalid byte sequences
        if key.to_s.bytesize > MAX_KEY_LENGTH
          raise ArgumentError, "too long (#{key.bytesize} bytes) header: #{key[0, 30].inspect}..."
        end
        if value.to_s.bytesize > MAX_FIELD_LENGTH
          raise ArgumentError, "header #{key} has too long field value: #{value.bytesize}"
        end
        if value.count("\r\n") > 0
          raise ArgumentError, "header #{key} has field value #{value.inspect}, this cannot include CR/LF"
        end
        @header[key.downcase.to_s] = [value]
      end
    end
  end

Haven't got a solution or workaround yet, but thought i'd put this here in case it helps someone. Will report back once I do have a solution, whatever it is.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions