From 16703766f4f42dcce4edd3ec32187efd9f10dcbf Mon Sep 17 00:00:00 2001 From: Arve Knudsen Date: Fri, 9 Jan 2026 10:04:37 +0100 Subject: [PATCH] promql: fix info() returning empty when filtering by overlapping labels (#17817) When filtering by a label that exists on both the input metric and target_info (e.g., info(metric, {host_name="orbstack"}) where host_name exists on both), the function incorrectly returned empty results. The bug was in combineWithInfoVector: when no new labels were added (because they all overlapped with base metric labels), the code entered the "no match" filtering block even though an info series WAS matched. The fix checks len(seenInfoMetrics) == 0 to correctly identify when no info series matched. If an info series matched (seenInfoMetrics is non-empty), the series is kept even if no new labels were added. Fixes #17813 Signed-off-by: Arve Knudsen --- promql/info.go | 7 ++++--- promql/promqltest/testdata/info.test | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/promql/info.go b/promql/info.go index ab4250104d..204ac44b40 100644 --- a/promql/info.go +++ b/promql/info.go @@ -424,9 +424,10 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[u } infoLbls := enh.lb.Labels() - if infoLbls.Len() == 0 { - // If there's at least one data label matcher not matching the empty string, - // we have to ignore this series as there are no matching info series. + if len(seenInfoMetrics) == 0 { + // No info series matched this base series. If there's at least one data + // label matcher not matching the empty string, we have to ignore this + // series as there are no matching info series. allMatchersMatchEmpty := true for _, ms := range dataLabelMatchers { for _, m := range ms { diff --git a/promql/promqltest/testdata/info.test b/promql/promqltest/testdata/info.test index 891e0eaa53..e15a429675 100644 --- a/promql/promqltest/testdata/info.test +++ b/promql/promqltest/testdata/info.test @@ -34,6 +34,22 @@ eval range from 0m to 10m step 5m info(metric, {data=~".+", non_existent=~".*"}) eval range from 0m to 10m step 5m info(metric_with_overlapping_label) metric_with_overlapping_label{data="base", instance="a", job="1", label="value", another_data="another info"} 0 1 2 +# Filtering by a label that exists on both base metric and target_info should work. +# This is a regression test for https://github.com/prometheus/prometheus/issues/17813. +# Note: data="base" on base metric, data="info" on target_info - the filter matches target_info. +eval range from 0m to 10m step 5m info(metric_with_overlapping_label, {data="info"}) + metric_with_overlapping_label{data="base", instance="a", job="1", label="value"} 0 1 2 + +# Filtering by a label that exists on both base metric and target_info with regex should work. +eval range from 0m to 10m step 5m info(metric_with_overlapping_label, {data=~".+"}) + metric_with_overlapping_label{data="base", instance="a", job="1", label="value"} 0 1 2 + +# Filtering by a label that exists on both base metric and target_info with same value. +# The selector matches the target_info, and the join succeeds via identifying labels. +# Note: Only the instance label is considered for inclusion, but it already exists on base. +eval range from 0m to 10m step 5m info(metric_with_overlapping_label, {instance="a"}) + metric_with_overlapping_label{data="base", instance="a", job="1", label="value"} 0 1 2 + # Include data labels from target_info specifically. eval range from 0m to 10m step 5m info(metric, {__name__="target_info"}) metric{data="info", instance="a", job="1", label="value", another_data="another info"} 0 1 2