temporalio/temporal · error

unable to create Elasticsearch client (URL = %v, username =

Error message

unable to create Elasticsearch client (URL = %v, username = %q): %w

What it means

Wraps a failure from esclient.NewClient while constructing the Elasticsearch client used by Temporal's visibility store during server bootstrap. The wrapper includes the redacted ES URL and username to help identify the misconfigured datastore. Temporal cannot start visibility persistence until a valid ES client is built, so this aborts startup.

Source

Thrown at temporal/fx.go:282

	if persistenceConfig.VisibilityConfigExist() &&
		persistenceConfig.DataStores[persistenceConfig.VisibilityStore].Elasticsearch != nil {
		esConfig = persistenceConfig.DataStores[persistenceConfig.VisibilityStore].Elasticsearch
		esConfig.SetHttpClient(so.elasticsearchHttpClient)
	}

	if esConfig != nil {
		esHttpClient := so.elasticsearchHttpClient
		if esHttpClient == nil {
			var err error
			esHttpClient, err = esclient.NewAwsHttpClient(esConfig.AWSRequestSigning)
			if err != nil {
				return serverOptionsProvider{}, fmt.Errorf("unable to create AWS HTTP client for Elasticsearch: %w", err)
			}
		}

		esClient, err = esclient.NewClient(esConfig, esHttpClient, logger)
		if err != nil {
			return serverOptionsProvider{}, fmt.Errorf("unable to create Elasticsearch client (URL = %v, username = %q): %w",
				esConfig.URL.Redacted(), esConfig.Username, err)
		}
	}

	// check that when static hosts are defined, they are defined for all required hosts
	if len(so.hostsByService) > 0 {
		for _, service := range DefaultServices {
			hosts := so.hostsByService[primitives.ServiceName(service)]
			if len(hosts.All) == 0 {
				return serverOptionsProvider{}, fmt.Errorf("%w: %v", missingServiceInStaticHosts, service)
			}
		}
	}

	if so.config.Global.Authorization.RemoteClusterAuth.Require && so.tokenProvider == nil {
		return serverOptionsProvider{}, errors.New("global.authorization.remoteClusterAuth.require is true but no TokenProvider is configured: use WithTokenProvider")
	}
	// TokenCredentials require TLS (RFC 9700); without a remote-cluster TLS source the first

View on GitHub (pinned to bde624efd1)

Solutions

  1. Fix the elasticsearch.url in the persistence datastore config (must be a valid, parseable URL)
  2. Verify elasticsearch username/password and AWSRequestSigning settings are consistent
  3. Check the wrapped root error (%w) for the underlying cause from the ES client library
  4. Test the ES endpoint is reachable with curl before starting Temporal

Example fix

// before (config)
datastores:
  es-visibility:
    elasticsearch:
      url: "elasticsearchover:9200"   # invalid URL
// after
datastores:
  es-visibility:
    elasticsearch:
      url: "http://elasticsearch:9200"
      username: "admin"
      password: "secret"
Defensive patterns

Strategy: validation

Validate before calling

cfg := persistenceConfig.DataStores[store].Elasticsearch
if cfg == nil { return nil }
if _, err := url.Parse(string(cfg.URL)); err != nil {
    return fmt.Errorf("invalid elasticsearch url: %w", err)
}
if cfg.Username == "" && !cfg.AWSRequestSigning.IsSet() {
    return errors.New("elasticsearch credentials or AWS signing required")
}

Prevention

When it happens

Trigger: Calling temporal.NewServerFx (or NewServer) with a persistence datastore whose Elasticsearch section is set; esclient.NewClient fails, e.g. malformed URL, invalid config fields, or an unusable injected HTTP client.

Common situations: Typo or invalid scheme in the elasticsearch.url config value; missing credentials that the ES client constructor rejects; bad AWSRequestSigning settings; custom elasticsearchHttpClient injection erroring during construction.

Related errors


AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01). Data as JSON: /api/errors/2ce79689466d2cf5. Report an issue: GitHub.