Skip to content

Headers conflict with Elastic-Api-Version on serverless #2983

Description

@kaisecheng

Describe the bug

After upgrading the elasticsearch gem to v9, Logstash serverless tests started failing.

Elasticsearch::Client in v9.x sets compatible-with=9 in Content-Type and Accept headers via set_content_type!. When a client also sets the Elastic-Api-Version header (required for Elasticsearch Serverless), serverless rejects the request with HTTP 400:

The request includes both the [Elastic-Api-Version] header and a [compatible-with] parameter,
but it is not valid to include both of these in a request

These two versioning mechanisms serve different deployment models and are mutually exclusive on the server side:

  • compatible-with=N: REST API compatibility for self-managed/hosted major version upgrades
  • Elastic-Api-Version: 2023-10-31: date-based API versioning for serverless

The v9 client injects compatible-with=9 at initialization time (set_content_type!) unless the caller explicitly sets Content-Type and Accept headers. Callers that only set Elastic-Api-Version (as the Logstash plugins do for serverless) get the conflict.

Expected behavior

set_content_type! should not add compatible-with headers when Elastic-Api-Version is already present in transport_options[:headers].

Your Environment

  • Ruby Version: JRuby 10.0.3.0 (Ruby 3.4.5)
  • Elasticsearch client version: 9.4.1 / 9.4.2
  • elastic-transport version: 8.5.2
  • Elasticsearch: Serverless

Additional context

This affects Logstash plugins that connect to Elasticsearch Serverless:

Both plugins recently upgraded their elasticsearch gem dependency from < 9 to < 10 (input PR#252, filter PR#213), which resolved to elasticsearch 9.x. Their serverless integration tests now fail: https://buildkite.com/elastic/logstash-serverless-integration-testing/builds/1151

The root cause is in Elasticsearch::Client#set_content_type! (elasticsearch.rb):

def set_content_type!(arguments)
  headers = {}
  user_headers = arguments&.[](:transport_options)&.[](:headers)
  unless user_headers&.keys&.detect { |h| h =~ /content-?_?type/i }
    headers['content-type'] = 'application/vnd.elasticsearch+json; compatible-with=9'
  end
  unless user_headers&.keys&.detect { |h| h =~ /accept/i }
    headers['accept'] = 'application/vnd.elasticsearch+json; compatible-with=9'
  end
  set_header(headers, arguments) unless headers.empty?
end

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