jaegertracing/jaeger · error

multiple backend types found for metric storage

Error message

multiple backend types found for metric storage: %v

What it means

Same one-backend-per-definition rule as the trace variant, applied to metric storage: MetricBackend.Validate() gathers all populated backend types and rejects any metric backend entry that specifies more than one, returning this error with the list of types found.

Solutions

  1. Keep exactly one backend type field inside each named metric backend entry; remove the extra
  2. Define separate named metric backends if you genuinely want multiple types available, then reference one per role
  3. Use the %v list in the error to identify which two types collided and which config keys to delete
  4. Audit generated/merged config (print the effective YAML) before starting Jaeger

Example fix

// before (config.yaml)
jaeger:
  storage:
    metric_backends:
      metrics:
        prometheus:
          host: "http://prom:9090"
        clickhouse:
          address: clickhouse:9000
// after
jaeger:
  storage:
    metric_backends:
      metrics:
        prometheus:
          host: "http://prom:9090"
Defensive patterns

Strategy: validation

Validate before calling

var cfg storageconfig.Config
if err := v.Unmarshal(&cfg); err != nil { return err }
for name, b := range cfg.MetricBackends {
    types := 0
    for _, set := range []interface{}{b.Prometheus, b.ClickHouse} {
        if !reflect.ValueOf(set).IsNil() { types++ }
    }
    if types > 1 {
        return fmt.Errorf("metric backend %q sets %d storage types; keep exactly one", name, types)
    }
}

Try / catch

if err := cfg.Validate(); err != nil {
    if strings.Contains(err.Error(), "multiple backend types found for metric storage") {
        // fix the named metric_backends entry named in the message
    }
    return err
}

Prevention

When it happens

Trigger: Calling Config.Validate() with a MetricBackends entry that has two or more backend fields populated, e.g. both `prometheus:` (remote storage config) and `clickhouse:` set on the same named metric backend.

Common situations: Config merge/Helm values layering that adds a second metric backend type to an existing entry; example configs combined by hand; leftovers of a migration from one metrics backend to another.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01). Data as JSON: /api/errors/c5862baaa162c862. Report an issue: GitHub.

Appendix: source

Thrown at cmd/internal/storageconfig/config.go:178

func (cfg *MetricBackend) Validate() error {
	var backends []string
	if cfg.Prometheus != nil {
		backends = append(backends, "prometheus")
	}
	if cfg.Elasticsearch != nil {
		backends = append(backends, "elasticsearch")
	}
	if cfg.Opensearch != nil {
		backends = append(backends, "opensearch")
	}
	if cfg.ClickHouse != nil {
		backends = append(backends, "clickhouse")
	}
	if len(backends) == 0 {
		return errors.New("empty configuration")
	}
	if len(backends) > 1 {
		return fmt.Errorf("multiple backend types found for metric storage: %v", backends)
	}
	return nil
}

// Validate validates the storage configuration.
func (c *Config) Validate() error {
	if len(c.TraceBackends) == 0 {
		return errors.New("at least one storage backend is required")
	}
	for name, b := range c.TraceBackends {
		if err := b.Validate(); err != nil {
			return fmt.Errorf("trace storage '%s': %w", name, err)
		}
	}
	for name, b := range c.MetricBackends {
		if err := b.Validate(); err != nil {
			return fmt.Errorf("metric storage '%s': %w", name, err)
		}

View on GitHub (pinned to 806f444784)