Skip to content

Reject repeated histogram bucket bounds - #1214

Open
chrikrah wants to merge 1 commit into
prometheus:masterfrom
chrikrah:chrikrah/histogram-strictly-increasing-buckets
Open

chrikrah wants to merge 1 commit into
prometheus:masterfrom
chrikrah:chrikrah/histogram-strictly-increasing-buckets

Conversation

@chrikrah

@chrikrah chrikrah commented Oct 4, 2026

Copy link
Copy Markdown

A NaN bucket bound passes Histogram._prepare_buckets (prometheus_client/metrics.py:642), because buckets != sorted(buckets) never fires on it. The histogram then exposes an le="NaN" series that no observation can land in. This change requires strictly increasing bounds: [0.1, nan, 1.0] and a repeated bound such as [0.1, 0.5, 0.5, 1.0] now raise ValueError at construction. No issue exists for this.

Before, at 9cd073c:

# Python 3.12.3; buckets=[0.1, float('nan'), 1.0], observe(0.05), observe(0.7)
$ PYTHONPATH=. python3 nan_bucket.py | grep _bucket
h_bucket{le="0.1"} 1.0
h_bucket{le="NaN"} 1.0
h_bucket{le="1.0"} 2.0
h_bucket{le="+Inf"} 2.0

The repeated bound is the part to decide. Prometheus ingests it today, since both le="0.5" series always carry the same value. Raising there buys consistency with client_golang ("histogram buckets must be in increasing order", prometheus/histogram.go:590), not ingestion safety. Code that passes one today gets a ValueError with this change. A lone [nan] still passes, as the check compares pairs.

Verification

# Python 3.14.7
$ python -m pytest -q tests
428 passed, 1 skipped in 10.21s
# base 9cd073c: 428 passed, 1 skipped (the new cases are assertions inside test_setting_buckets)
$ git stash -- prometheus_client/metrics.py && python -m pytest -q tests/test_core.py   # fix reverted, test kept
FAILED tests/test_core.py::TestHistogram::test_setting_buckets - AssertionError: ValueError not raised by Histogram
1 failed, 113 passed in 3.73s
$ flake8 prometheus_client/ tests/ && isort --check prometheus_client/ tests/   # flake8 7.4.1, isort 5.10.1

This touches the same function as #1104. I will rebase onto whichever lands first.

@csmarchbanks, should a repeated bound raise as here, or be dropped silently as client_java does?

_prepare_buckets accepted any non-decreasing list, so buckets=[0.1, 0.5,
0.5, 1.0] exposed two series with le="0.5" and a NaN bound passed too.
Require strictly increasing bounds, as client_golang does.

Signed-off-by: Christopher Krah <[email protected]>

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant