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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions