Skip to content

fix(rpc): fix seer RPC exception handling to default to 500 and preserve rest APIException#112435

Open
aliu39 wants to merge 1 commit intomasterfrom
cursor/seer-rpc-rest-framework-exceptions-b045
Open

fix(rpc): fix seer RPC exception handling to default to 500 and preserve rest APIException#112435
aliu39 wants to merge 1 commit intomasterfrom
cursor/seer-rpc-rest-framework-exceptions-b045

Conversation

@aliu39
Copy link
Copy Markdown
Member

@aliu39 aliu39 commented Apr 7, 2026

Problem

When Seer makes RPC calls to Sentry and encounters downstream errors (like 429 rate limits or 500 errors from Snuba), the current code catches all exceptions and re-raises them as ValidationError (400). This misrepresents the actual issue, making it appear as if the client sent bad request parameters when the real cause is a downstream service error.

Example issue: https://sentry.dev.getsentry.net:7999/issues/7380803020/events/d3e6e1d5ddf44d7b94c86e6ce2435ea6/

Solution

This PR fixes the exception handling in the seer RPC endpoint to raise a generic 500 APIException by default, implying an error happened on sentry server. Also re-raises APIException's (base class for rest framework excs) so developers can bubble their own status codes up to response for Seer.

  1. Added a handler to re-raise REST framework exceptions directly - Any APIException (which includes Throttled, custom API exceptions, etc.) is now re-raised as-is, preserving its status code
  2. Changed generic exception handler to raise APIException (500) instead of ValidationError (400) - Unknown errors now correctly return 500 Internal Server Error instead of 400 Bad Request

Changes

  • Modified SeerRpcServiceEndpoint.post() in src/sentry/seer/endpoints/seer_rpc.py:
    • Added except APIException: raise handler before the generic exception handler
    • Changed raise ValidationError from e to raise APIException from e for generic exceptions
  • Added test cases in tests/sentry/seer/endpoints/test_seer_rpc.py:
    • test_rest_framework_exceptions_are_reraised - verifies custom API exceptions preserve their status codes
    • test_generic_exceptions_return_500 - verifies generic exceptions return 500 instead of 400

Testing

Pre-commit checks pass ✅

Slack Thread

Open in Web Open in Cursor 

Previously, all exceptions from downstream services (Sentry/Snuba) were
being caught and re-raised as ValidationError (400), which incorrectly
represented issues like rate limits (429) or internal errors (500) as
bad request parameters.

This change:
- Adds a handler to re-raise any REST framework APIException directly,
  preserving their status codes
- Changes the generic exception handler to raise APIException (500)
  instead of ValidationError (400)
- Adds tests to verify that REST framework exceptions are properly
  re-raised and generic exceptions return 500

This allows Seer to properly differentiate between client errors (4xx)
and server errors (5xx) from downstream services.

Co-authored-by: Andrew Liu <aliu39@users.noreply.github.com>
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Apr 7, 2026
@aliu39 aliu39 marked this pull request as ready for review April 8, 2026 00:26
@aliu39 aliu39 requested a review from a team as a code owner April 8, 2026 00:26
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Test doesn't exercise the new APIException code path
    • Mocked in_test_environment() to return False so the test properly exercises the APIException conversion code path instead of re-raising the original exception.

Create PR

Or push these changes by commenting:

@cursor push bbfaa3b8be
Preview (bbfaa3b8be)
diff --git a/tests/sentry/seer/endpoints/test_seer_rpc.py b/tests/sentry/seer/endpoints/test_seer_rpc.py
--- a/tests/sentry/seer/endpoints/test_seer_rpc.py
+++ b/tests/sentry/seer/endpoints/test_seer_rpc.py
@@ -112,9 +112,12 @@
         path = self._get_path("get_organization_slug")
         data: dict[str, Any] = {"args": {"org_id": 1}, "meta": {}}
 
-        with patch(
-            "sentry.seer.endpoints.seer_rpc.SeerRpcServiceEndpoint._dispatch_to_local_method"
-        ) as mock_dispatch:
+        with (
+            patch(
+                "sentry.seer.endpoints.seer_rpc.SeerRpcServiceEndpoint._dispatch_to_local_method"
+            ) as mock_dispatch,
+            patch("sentry.seer.endpoints.seer_rpc.in_test_environment", return_value=False),
+        ):
             mock_dispatch.side_effect = RuntimeError("Unexpected internal error")
 
             response = self.client.post(

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 388d7ab. Configure here.

@aliu39 aliu39 changed the title Fix seer RPC exception handling to preserve HTTP status codes fix(rpc): fix seer RPC exception handling to default to 500 and preserve rest APIException Apr 8, 2026
@aliu39
Copy link
Copy Markdown
Member Author

aliu39 commented Apr 8, 2026

Re cursor: not sure how to simulate non-test environment from a test.. code seems straightforward enough to not cover

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

Labels

Scope: Backend Automatically applied to PRs that change backend components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants