Skip to content

Histogram explicit_bucket_boundaries_advisory validation misses unsorted/non-finite boundaries, which then raise ValueError out of every record() call #5844

Open

Description

@RudraDudhat2509

What happened

Meter.create_histogram()'s validation of explicit_bucket_boundaries_advisory only checks that every element is a float/int. It never checks the values are finite or strictly increasing, even though _ExplicitBucketHistogramAggregation.__init__ (the real aggregation, built the first time the histogram is recorded) raises ValueError for exactly that. So a type-valid but unsorted/non-finite advisory passes create_histogram() silently, with no warning, then raises uncaught out of the user's own record() call the first time it's used, and keeps raising on every subsequent call, since the failed match is never cached.

Reproducer

from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import InMemoryMetricReader

reader = InMemoryMetricReader()
meter = MeterProvider(metric_readers=[reader]).get_meter("test")

h = meter.create_histogram("latency", explicit_bucket_boundaries_advisory=[100, 10, 50])
# no warning here, even though the advisory is unsorted

h.record(5)
# ValueError: boundaries must be strictly increasing
h.record(5)
# raises again, every single call, forever

Root cause

sdk/metrics/_internal/__init__.py, Meter.create_histogram only checks isinstance(e, (float, int)) for each boundary. The real check (finite, strictly increasing) lives in _ExplicitBucketHistogramAggregation.__init__, which only runs lazily the first time a measurement is recorded (via _ViewInstrumentMatch.__init__, constructed from MetricReaderStorage._get_or_init_view_instrument_match). That construction raises before the match gets cached, so every later record() call re-attempts it and re-raises.

Proposed fix

Extend create_histogram's existing advisory check to also validate finite + strictly increasing, the same predicate _ExplicitBucketHistogramAggregation already uses, folded into the existing "drop the advisory with a warning" path instead of letting it reach record(). Have a fix + tests ready, opening a PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions