Skip to content

Commit 506518c

Browse files
authored
chore(seer rpc): Pass along error message (#103701)
- Pass along specific error from the tool call for agent
1 parent 22077bd commit 506518c

File tree

3 files changed

+85
-18
lines changed

3 files changed

+85
-18
lines changed

src/sentry/seer/assisted_query/issues_tools.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ def execute_issues_query(
479479
stats_period: str = "7d",
480480
sort: str | None = None,
481481
limit: int = 25,
482-
) -> list[dict[str, Any]] | None:
482+
) -> list[dict[str, Any]] | dict[str, Any] | None:
483483
"""
484484
Execute an issues query by calling the issues endpoint.
485485
@@ -492,7 +492,7 @@ def execute_issues_query(
492492
limit: Number of results to return (default 25)
493493
494494
Returns:
495-
List of issues, or None if organization doesn't exist
495+
List of issues, dict with error key if 400 error occurred, or None if organization doesn't exist
496496
"""
497497
try:
498498
organization = Organization.objects.get(id=org_id)
@@ -515,14 +515,19 @@ def execute_issues_query(
515515
if sort:
516516
params["sort"] = sort
517517

518-
resp = client.get(
519-
auth=api_key,
520-
user=None,
521-
path=f"/organizations/{organization.slug}/issues/",
522-
params=params,
523-
)
524-
525-
return resp.data
518+
try:
519+
resp = client.get(
520+
auth=api_key,
521+
user=None,
522+
path=f"/organizations/{organization.slug}/issues/",
523+
params=params,
524+
)
525+
return resp.data
526+
except client.ApiError as e:
527+
if e.status_code == 400:
528+
error_detail = e.body.get("detail") if isinstance(e.body, dict) else None
529+
return {"error": str(error_detail) if error_detail is not None else str(e.body)}
530+
raise
526531

527532

528533
def get_issues_stats(

src/sentry/seer/explorer/tools.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,19 @@ def execute_table_query(
9292
# Remove None values
9393
params = {k: v for k, v in params.items() if v is not None}
9494

95-
# Call sentry API client. This will raise API errors for non-2xx / 3xx status.
96-
resp = client.get(
97-
auth=ApiKey(organization_id=organization.id, scope_list=["org:read", "project:read"]),
98-
user=None,
99-
path=f"/organizations/{organization.slug}/events/",
100-
params=params,
101-
)
102-
return {"data": resp.data["data"]}
95+
try:
96+
resp = client.get(
97+
auth=ApiKey(organization_id=organization.id, scope_list=["org:read", "project:read"]),
98+
user=None,
99+
path=f"/organizations/{organization.slug}/events/",
100+
params=params,
101+
)
102+
return {"data": resp.data["data"]}
103+
except client.ApiError as e:
104+
if e.status_code == 400:
105+
error_detail = e.body.get("detail") if isinstance(e.body, dict) else None
106+
return {"error": str(error_detail) if error_detail is not None else str(e.body)}
107+
raise
103108

104109

105110
def execute_timeseries_query(

tests/sentry/seer/explorer/test_tools.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,63 @@ def test_spans_query_nonexistent_organization(self):
320320
)
321321
assert table_result is None
322322

323+
@patch("sentry.seer.explorer.tools.client.get")
324+
def test_spans_table_query_error_handling(self, mock_client_get):
325+
"""Test error handling for API errors: 400 errors return error dict, non-400 errors are re-raised"""
326+
# Test 400 error with dict body containing detail
327+
error_detail_msg = "Invalid query: field 'invalid_field' does not exist"
328+
mock_client_get.side_effect = client.ApiError(400, {"detail": error_detail_msg})
329+
330+
result = execute_table_query(
331+
org_id=self.organization.id,
332+
dataset="spans",
333+
fields=self.default_span_fields,
334+
query="invalid_field:value",
335+
stats_period="1h",
336+
sort="-timestamp",
337+
per_page=10,
338+
)
339+
340+
assert result is not None
341+
assert "error" in result
342+
assert result["error"] == error_detail_msg
343+
assert "data" not in result
344+
345+
# Test 400 error with string body
346+
error_body = "Bad request: malformed query syntax"
347+
mock_client_get.side_effect = client.ApiError(400, error_body)
348+
349+
result = execute_table_query(
350+
org_id=self.organization.id,
351+
dataset="spans",
352+
fields=self.default_span_fields,
353+
query="malformed query",
354+
stats_period="1h",
355+
sort="-timestamp",
356+
per_page=10,
357+
)
358+
359+
assert result is not None
360+
assert "error" in result
361+
assert result["error"] == error_body
362+
assert "data" not in result
363+
364+
# Test non-400 errors are re-raised
365+
mock_client_get.side_effect = client.ApiError(500, {"detail": "Internal server error"})
366+
367+
with pytest.raises(client.ApiError) as exc_info:
368+
execute_table_query(
369+
org_id=self.organization.id,
370+
dataset="spans",
371+
fields=self.default_span_fields,
372+
query="",
373+
stats_period="1h",
374+
sort="-timestamp",
375+
per_page=10,
376+
)
377+
378+
assert exc_info.value.status_code == 500
379+
323380
def test_spans_timeseries_with_groupby(self):
324381
"""Test timeseries query with group_by parameter for aggregates"""
325382
result = execute_timeseries_query(

0 commit comments

Comments
 (0)