NRE on ElasticsearchClient

Im having a hard time connecting to elasticsearch form my dotnet 9 application.

System.NullReferenceException: Object reference not set to an instance of an object.
   at Elastic.Transport.DistributedTransport`1.RequestCoreAsync[TResponse](Boolean isAsync, EndpointPath path, PostData data, Action`1 configureActivity, IRequestConfiguration localConfiguration, CancellationToken cancellationToken)
   at Elastic.Clients.Elasticsearch.ElasticsearchClient.<>c__DisplayClass865_0`3.<<DoRequestCoreAsync>g__SendRequestWithProductCheckCore|2>d.MoveNext() in /_/src/Elastic.Clients.Elasticsearch/_Shared/Client/ElasticsearchClient.cs:line 221
--- End of stack trace from previous location ---
   at Elastic.Clients.Elasticsearch.ElasticsearchClient.<>c__DisplayClass865_0`3.<<DoRequestCoreAsync>g__SendRequestWithProductCheck|1>d.MoveNext() in /_/src/Elastic.Clients.Elasticsearch/_Shared/Client/ElasticsearchClient.cs:line 180
--- End of stack trace from previous location ---
   at Rust.Domain.Infrastructure.Searching.SearchProvider.HealthyAsync(CancellationToken cancellationToken) in /src/src/Rust.Domain/Infrastructure/Searching/SearchProvider.cs:line 21
   at Rust.GameserverIndexer.GameserverIndexer.EnsureIndexRebuiltAsync(CancellationToken cancellationToken) in /src/src/Rust.GameserverIndexer/GameserverIndexer.cs:line 64
   at Rust.GameserverIndexer.GameserverIndexer.EnsureIndexRebuiltAsync(CancellationToken cancellationToken) in /src/src/Rust.GameserverIndexer/GameserverIndexer.cs:line 67
   at Rust.GameserverIndexer.GameserverIndexer.ExecuteAsync(CancellationToken stoppingToken) in /src/src/Rust.GameserverIndexer/GameserverIndexer.cs:line 39
   at Microsoft.Extensions.Hosting.Internal.Host.TryExecuteBackgroundServiceAsync(BackgroundService backgroundService)

Very, very similar issues on both 9.0.1 and 8.17.1, I downgraded to check which makes me think this is a skill issue.

Im currently using Microsoft.Extensions.DependencyInjection and registering the client as a singleton:

            builder.Services.AddSingleton(sp =>
            {
                IConfiguration configuration = sp.GetRequiredService<IConfiguration>();
                ILogger<ElasticsearchClient> logger = sp.GetRequiredService<ILogger<ElasticsearchClient>>();

                Uri[] nodeUris = configuration.GetValue<string>("ELASTIC_NODES")?
                    .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
                    .Select(uri => new Uri(uri))
                    .ToArray() ?? throw new InvalidOperationException("ELASTIC_NODES configuration is missing or invalid.");

                if (nodeUris.Length == 0)
                    throw new InvalidOperationException("No valid ELASTIC_NODES provided.");

                string apiKey = configuration.GetValue<string>("ELASTIC_APIKEY")
                    ?? throw new InvalidOperationException("ELASTIC_APIKEY configuration is missing.");

                ElasticsearchClientSettings settings = new ElasticsearchClientSettings(new StaticNodePool(nodeUris))
                    .Authentication(new Base64ApiKey(apiKey))
                    .ServerCertificateValidationCallback((_, _, _, _) => true)
                    .ThrowExceptions(true)

#if DEBUG
                    .EnableDebugMode(x =>
                    {
                        logger.LogDebug(x.DebugInformation);
                    })
#endif
                    ;

                return new ElasticsearchClient(settings);
            });

And using it in my service which is registered as transient:

    public class SearchProvider : ISearchProvider
    {
        private readonly ILogger<SearchProvider> logger;
        private readonly ElasticsearchClient _client;

        public SearchProvider(ILogger<SearchProvider> logger, ElasticsearchClient client)
        {
            this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
            _client = client ?? throw new ArgumentNullException(nameof(client));
        }

        public async Task<bool> HealthyAsync(CancellationToken cancellationToken)
        {
            Elastic.Clients.Elasticsearch.Cluster.HealthResponse health = await _client.Cluster.HealthAsync(cancellationToken);
            if (!health.IsValidResponse)
            {
                throw new Exception($"Elasticsearch health check failed: {health.DebugInformation}");
            }
            return health.Status == HealthStatus.Green;
        }
}

My app starts a hosted service which the first thing it does is call the HealthyAsync method, which throws the above error.

I feel like im missing a step. anyone got ideas?

This may very well be a UI bug, changed apikey to correct (or known good) and error has subsided using 8.17.1.

I may make a GH issue.