thanos-io/thanos · warning

exponential histogram data point has zero count, but…

Error message

exponential histogram data point has zero count, but non-zero sum: %f

What it means

While converting exponential histograms, a data point with Count()==0 but a non-zero Sum is semantically contradictory. The code cannot error without losing the metric, so it records this message as an annotations.Annotations warning attached to the conversion result.

Solutions

  1. Fix the telemetry source so count and sum are consistent (count 0 must imply sum 0)
  2. Check aggregators/proxies that may drop bucket counts while retaining the sum after resets
  3. Treat it as a warning annotation — the receiver usually converts it anyway; verify whether your pipeline drops annotated metrics
  4. Upgrade the SDK; some versions had count/sum mismatch bugs on resets
Defensive patterns

Strategy: validation

Validate before calling

if dp.Count() == 0 && dp.HasSum() && dp.Sum() != 0 {
    return errors.New("exponential histogram count/sum mismatch")
}

Type guard

func countSumConsistent(dp pmetric.ExponentialHistogramDataPoint) bool {
    return dp.Count() != 0 || !dp.HasSum() || dp.Sum() == 0
}

Try / catch

h, annots, err := exponentialToNativeHistogram(dp)
for _, a := range annots {
    log.Warnf("annotation from conversion: %v", a) // includes zero-count/non-zero-sum
}

Prevention

When it happens

Trigger: OTLP ExponentialHistogram data point where Count is 0 but Sum != 0, produced by instruments that track sums separately from counts (e.g. reset counters mid-window, or sum-only aggregation).

Common situations: Counter resets in backend aggregation layers; buggy custom aggregators that emit sum without count; OTLP from languages where sum is a float and count is tracked independently and initialized late.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/0f859ec9fc2f6886. Report an issue: GitHub.

Appendix: source

Thrown at pkg/receive/otlptranslator/histograms.go:124

		PositiveSpans:  pSpans,
		PositiveDeltas: pDeltas,
		NegativeSpans:  nSpans,
		NegativeDeltas: nDeltas,

		Timestamp: convertTimeStamp(p.Timestamp()),
	}

	if p.Flags().NoRecordedValue() {
		h.Sum = math.Float64frombits(value.StaleNaN)
		h.Count = &prompb.Histogram_CountInt{CountInt: value.StaleNaN}
	} else {
		if p.HasSum() {
			h.Sum = p.Sum()
		}
		h.Count = &prompb.Histogram_CountInt{CountInt: p.Count()}
		if p.Count() == 0 && h.Sum != 0 {
			annots.Add(fmt.Errorf("exponential histogram data point has zero count, but non-zero sum: %f", h.Sum))
		}
	}
	return h, annots, nil
}

// convertBucketsLayout translates OTel Exponential Histogram dense buckets
// representation to Prometheus Native Histogram sparse bucket representation.
//
// The translation logic is taken from the client_golang `histogram.go#makeBuckets`
// function, see `makeBuckets` https://github.com/prometheus/client_golang/blob/main/prometheus/histogram.go
// The bucket indexes conversion was adjusted, since OTel exp. histogram bucket
// index 0 corresponds to the range (1, base] while Prometheus bucket index 0
// to the range (base 1].
//
// scaleDown is the factor by which the buckets are scaled down. In other words 2^scaleDown buckets will be merged into one.
func convertBucketsLayout(buckets pmetric.ExponentialHistogramDataPointBuckets, scaleDown int32) ([]prompb.BucketSpan, []int64) {
	bucketCounts := buckets.BucketCounts()
	if bucketCounts.Len() == 0 {

View on GitHub (pinned to 35b8b99117)