C# Elasticsearch 8.0.3+ 400 Bad Request media_type_header_exception

Just started with a new small project with Elasticsearch, while developing Elasticsearch it is currently running in Docker (docker-elk), with the added "http.cors" things:

http.cors.enabled: true
http.cors.allow-origin: "https://app.elasticvue.com"
http.cors.allow-headers: X-Requested-With,Content-Type,Content-Length,Authorization

Since version 8.0.3 it will send REST API compatibility headers:

Release notes v8.0.3 | Elasticsearch .NET Client [8.0] | Elastic
REST API compatibility | Elasticsearch Guide [8.5] | Elastic

Somehow it doesn't correctly retrieve/send the correct version, and Elasticsearch returns a 400 Bad Request because of this.

Maybe the same is/was in the Java version? PR on GitHub, but I'm not sure.

This is the response I get is:

{
	"error": {
		"root_cause": [
			{
				"type": "media_type_header_exception",
				"reason": "Invalid media-type value on headers [Accept, Content-Type]"
			}
		],
		"type": "media_type_header_exception",
		"reason": "Invalid media-type value on headers [Accept, Content-Type]",
		"caused_by": {
			"type": "status_exception",
			"reason": "Accept version must be either version 8 or 7, but found 0. Accept=application/vnd.elasticsearch+json;compatible-with=0"
		}
	},
	"status": 400
}

So it doesn't see a version.

When running it in a testcase, it works. But when deployed it doesn't.

The .NET version is, sadly, .NET Framework 4.8, which is a limitation, cannot upgrade it to .NET (Core) because it's a SAAS.

When I revert back to 8.0.2, it works fine.

Should I make a bug on GitHub?

Kind regards!

Hi, @KoalaBear.

If this works locally but not when deployed, possibly something in the way it is deployed is breaking the logic which loads the version from our assembly info. We saw this for a user who was creating a single DLL causing the version lookup to fail. Are you packaging your application in an unusual way that might cause a similar behaviour?

Hi Steve,

That's some good thoughts :+1: Yes and no. :sweat_smile:

We do use ILmerge for some of our own DLL, but the Elasticsearch one's are not merged at all.

Couldn't paste an image, so:

 Length Name
 ------ ----
4018176 Elastic.Clients.Elasticsearch.dll
 253440 Elastic.Transport.dll
  22144 Microsoft.Bcl.AsyncInterfaces.dll
 327168 OurDlls.Merged.dll
 273920 StructureMap.dll
  98184 System.Diagnostics.DiagnosticSource.dll
  18024 System.Runtime.CompilerServices.Unsafe.dll
  76904 System.Text.Encodings.Web.dll
 513128 System.Text.Json.dll

So it should be able to get the versions from these DLLs. I will (ASAP) try to run those version 'retrieval' lines deployed and see what it returns and come back to you. Thanks!

OK, I don't use NEST of course, but I've tried it with ElasticsearchClient and it didn't find the assembly location. :partying_face:

string location = typeof(ElasticsearchClient).GetTypeInfo().Assembly.Location;
Context.Log(LogLevel.Error, $"ElasticsearchClient assembly location: {location}");

string version = FileVersionInfo.GetVersionInfo(location)?.ProductVersion;
Context.Log(LogLevel.Error, $"ElasticsearchClient assembly version: {version}");

It's running on a SAAS and I know it's running inside Azure Service Fabric, that's all I know, so it might be security permissions of some sort.

Is there anything else I can try to give more debug info?

Tried some more things, only the GetEntryAssembly locations is returning anything, but that is NOT 'our' DLL. So cannot even get our own version in this way.

GetExecutingAssembly location: 
Error GetExecutingAssembly version: The path is not of a legal form.

GetEntryAssembly location: D:\SvcFab\***\ConnectExecutorActor.exe
GetEntryAssembly version: 1.0.0

GetCallingAssembly location: 
Error GetCallingAssembly version: The path is not of a legal form.

ElasticsearchClient assembly location: 
Error getting assembly version: The path is not of a legal form.

Code:

try
{
	Assembly executingAssembly = Assembly.GetExecutingAssembly();
	string executingAssemblyLocation = executingAssembly.Location;
	Context.Log(LogLevel.Error, $"GetExecutingAssembly location: {executingAssemblyLocation}");
	string executingAssemblyVersion = FileVersionInfo.GetVersionInfo(executingAssemblyLocation)?.ProductVersion;
	Context.Log(LogLevel.Error, $"GetExecutingAssembly version: {executingAssemblyVersion}");
}
catch (Exception ex)
{
	Context.Log(LogLevel.Error, $"Error GetExecutingAssembly version: {ex.Message}");
}

try
{
	Assembly entryAssembly = Assembly.GetEntryAssembly();
	string entryAssemblyLocation = entryAssembly.Location;
	Context.Log(LogLevel.Error, $"GetEntryAssembly location: {entryAssemblyLocation}");
	string entryAssemblyVersion = FileVersionInfo.GetVersionInfo(entryAssemblyLocation)?.ProductVersion;
	Context.Log(LogLevel.Error, $"GetEntryAssembly version: {entryAssemblyVersion}");
}
catch (Exception ex)
{
	Context.Log(LogLevel.Error, $"Error GetEntryAssembly version: {ex.Message}");
}

try
{
	Assembly callingAssembly = Assembly.GetCallingAssembly();
	string callingAssemblyLocation = callingAssembly.Location;
	Context.Log(LogLevel.Error, $"GetCallingAssembly location: {callingAssemblyLocation}");
	string callingAssemblyVersion = FileVersionInfo.GetVersionInfo(callingAssemblyLocation)?.ProductVersion;
	Context.Log(LogLevel.Error, $"GetCallingAssembly version: {callingAssemblyVersion}");
}
catch (Exception ex)
{
	Context.Log(LogLevel.Error, $"Error GetCallingAssembly version: {ex.Message}");
}

try
{
	Assembly elasticAssembly = typeof(ElasticsearchClient).GetTypeInfo().Assembly;

	if (elasticAssembly is null)
	{
		Context.Log(LogLevel.Error, $"ElasticsearchClient assembly: NULL");
	}

	string elasticLocation = elasticAssembly.Location;
	Context.Log(LogLevel.Error, $"ElasticsearchClient assembly location: {elasticLocation}");

	string elasticVersion = FileVersionInfo.GetVersionInfo(elasticLocation)?.ProductVersion;
	Context.Log(LogLevel.Error, $"ElasticsearchClient assembly version: {elasticVersion}");
}
catch (Exception ex)
{
	Context.Log(LogLevel.Error, $"Error GetCallingAssembly version: {ex.Message}");
}

try
{
	Assembly elastic2Assembly = typeof(ElasticsearchClient).Assembly;

	if (elastic2Assembly is null)
	{
		Context.Log(LogLevel.Error, $"elastic2searchClient assembly: NULL");
	}

	string elastic2Location = elastic2Assembly.Location;
	Context.Log(LogLevel.Error, $"elastic2searchClient assembly location: {elastic2Location}");

	string elastic2Version = FileVersionInfo.GetVersionInfo(elastic2Location)?.ProductVersion;
	Context.Log(LogLevel.Error, $"elastic2searchClient assembly version: {elastic2Version}");
}
catch (Exception ex)
{
	Context.Log(LogLevel.Error, $"Error GetCallingAssembly version: {ex.Message}");
}

Sorry for the spamming :sweat_smile:

The only thing that works is:

Assembly elasticAssembly = typeof(ElasticsearchClient).Assembly;
string version = ((AssemblyFileVersionAttribute)Attribute.GetCustomAttribute(elasticAssembly, typeof(AssemblyFileVersionAttribute), false)).Version;
Version elasticVersion = new(version);
Context.Log(LogLevel.Error, $"GetCustomAttribute AssemblyFileVersionAttribute version: {version}, Major: {elasticVersion.Major}");

Result:

GetCustomAttribute AssemblyFileVersionAttribute version: 8.0.2.0, Major: 8

Also available as a oneliner:

Version elasticVersion = new(typeof(ElasticsearchClient).Assembly?.GetCustomAttribute<AssemblyFileVersionAttribute>().Version);

It's also better than using FileVersionInfo.GetVersionInfo() I think.

Could you check my replies? @stevejgordon

Thanks in advance!

Sorry, @KoalaBear.

Discuss doesn't notify on replies without the '@' mention, so I missed yours.

I've raised an issue because we should address this as a bug in the client. Thanks for digging into this in your scenario.

NP, I thought so :innocent: and did a gentle bump, great :+1:

1 Like