From a40f34a7e23c9b046510039f75b07f9980714bf3 Mon Sep 17 00:00:00 2001 From: 1AbdullahButt <168034895+1AbdullahButt@users.noreply.github.com> Date: Sun, 1 Nov 2026 20:10:09 +0500 Subject: [PATCH] fix(events-stats): Return 400 for EAP topEvents without a group-by A topEvents request on an RPC dataset (spans, logs, trace metrics) with only aggregate fields has no columns to group by. The top events conditions were then built from empty OR filters, which Snuba rejects, so the endpoint returned a 500 with no useful detail. Reject these requests up front with a 400 that says a non-aggregate field is needed, matching the existing "Cannot group by timestamp" check. Fixes #129342 Co-Authored-By: Claude Opus 5.5 --- .../endpoints/organization_events_stats.py | 3 +++ ..._organization_events_stats_span_indexed.py | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/sentry/api/endpoints/organization_events_stats.py b/src/sentry/api/endpoints/organization_events_stats.py index f042624ba95d..6a3fd99ee188 100644 --- a/src/sentry/api/endpoints/organization_events_stats.py +++ b/src/sentry/api/endpoints/organization_events_stats.py @@ -29,6 +29,7 @@ get_trace_metric_from_request, ) from sentry.search.eap.types import SearchResolverConfig +from sentry.search.events.fields import is_function from sentry.search.events.types import SnubaParams from sentry.snuba import ( discover, @@ -305,6 +306,8 @@ def get_rpc_config(): if "timestamp" in raw_groupby: raise ParseError("Cannot group by timestamp") if use_rpc: + if all(is_function(column) for column in raw_groupby): + raise ParseError("topEvents requires at least one non-aggregate field") return scoped_dataset.run_top_events_timeseries_query( params=snuba_params, query_string=query, diff --git a/tests/snuba/api/endpoints/test_organization_events_stats_span_indexed.py b/tests/snuba/api/endpoints/test_organization_events_stats_span_indexed.py index 5728571ed1af..792fd67b711b 100644 --- a/tests/snuba/api/endpoints/test_organization_events_stats_span_indexed.py +++ b/tests/snuba/api/endpoints/test_organization_events_stats_span_indexed.py @@ -719,6 +719,32 @@ def test_top_events_with_no_data(self) -> None: ) assert response.status_code == 200, response.content + def test_top_events_without_groupby(self) -> None: + self.store_spans( + [ + self.create_span( + {"sentry_tags": {"op": "db"}}, + start_ts=self.day_ago + timedelta(minutes=1), + ), + ], + ) + + response = self._do_request( + data={ + "start": self.day_ago, + "end": self.day_ago + timedelta(minutes=6), + "interval": "1m", + "yAxis": "p95(span.duration)", + "field": ["p95(span.duration)"], + "query": "span.op:db", + "dataset": "spans", + "excludeOther": 0, + "topEvents": 10, + }, + ) + assert response.status_code == 400, response.content + assert response.data["detail"] == "topEvents requires at least one non-aggregate field" + def test_count_unique_nans(self) -> None: self.store_span( self.create_span(start_ts=self.two_days_ago + timedelta(minutes=1)),