Skip to content

feat(seer): Detect Cursor GitHub access errors and return specific failure type#109692

Merged
isaacwang-sentry merged 10 commits intomasterfrom
iwang/cursor-github-access-backend
Mar 4, 2026
Merged

feat(seer): Detect Cursor GitHub access errors and return specific failure type#109692
isaacwang-sentry merged 10 commits intomasterfrom
iwang/cursor-github-access-backend

Conversation

@isaacwang-sentry
Copy link
Copy Markdown
Member

Detect when Cursor's coding agent returns a 400 error with "Failed to verify existence of branch" and surface a cursor_github_access failure type instead of falling through to the generic error handler.

This allows the frontend to show a targeted prompt asking the user to install the Cursor GitHub App, rather than a confusing generic error message.

Changes:

  • coding_agent.py: Add branch-verification error detection with an installation is not None guard (only applies to Cursor, not GitHub Copilot which has no installation)
  • coding_agent_handoff.py: Restructure the isinstance(e, ApiError) block to check for this error when not is_github_copilot
  • Add tests for both code paths

…ilure type

When Cursor returns a 400 error with "Failed to verify existence of branch",
surface a cursor_github_access failure type so the frontend can prompt the
user to install the Cursor GitHub App instead of showing a generic error.

Co-Authored-By: Claude <[email protected]>
@github-actions github-actions bot added the Scope: Backend Automatically applied to PRs that change backend components label Mar 2, 2026
@isaacwang-sentry isaacwang-sentry marked this pull request as ready for review March 2, 2026 19:46
@isaacwang-sentry isaacwang-sentry requested a review from a team as a code owner March 2, 2026 19:46
Copy link
Copy Markdown
Member

@JoshFerge JoshFerge left a comment

Choose a reason for hiding this comment

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

one small thought, but looks great, thanks!

…r_github_access failure

The error handling for "Failed to verify existence of branch" assumed any
non-null installation was Cursor. Explicitly check the provider to avoid
misattributing errors from other coding agent integrations.

Co-Authored-By: Claude <[email protected]>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 3, 2026

Backend Test Failures

Failures on cc87e71 in this run:

tests/sentry/seer/autofix/test_coding_agent.py::TestLaunchAgentsForRepos::test_verify_branch_error_returns_cursor_github_access_failure_typelog
tests/sentry/seer/autofix/test_coding_agent.py:310: in test_verify_branch_error_returns_cursor_github_access_failure_type
    assert failure["failure_type"] == "cursor_github_access"
E   AssertionError: assert 'generic' == 'cursor_github_access'
E     
E     - cursor_github_access
E     + generic
tests/sentry/integrations/api/endpoints/test_organization_coding_agents.py::OrganizationCodingAgentsPost403PermissionTest::test_org_installation_403_returns_generic_failure_typelog
tests/sentry/integrations/api/endpoints/test_organization_coding_agents.py:1084: in test_org_installation_403_returns_generic_failure_type
    response = self.get_success_response(self.organization.slug, method="post", **data)
src/sentry/testutils/cases.py:638: in get_success_response
    assert_status_code(response, 200, 300)
src/sentry/testutils/asserts.py:47: in assert_status_code
    assert minimum <= response.status_code < maximum, (
E   AssertionError: (500, b'{"detail":"Internal Error","errorId":null}')
E   assert 500 < 300
E    +  where 500 = <Response status_code=500, "application/json">.status_code

Apply the same provider check from coding_agent.py to
coding_agent_handoff.py — use `integration.provider == "cursor"`
instead of `not is_github_copilot` to avoid misattributing errors
from future non-Cursor providers. Also set mock provider in the
test so it matches the production check.

Co-Authored-By: Claude <[email protected]>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 3, 2026

Backend Test Failures

Failures on 962bfa7 in this run:

tests/sentry/integrations/api/endpoints/test_organization_coding_agents.py::OrganizationCodingAgentsPost403PermissionTest::test_org_installation_403_returns_generic_failure_typelog
tests/sentry/integrations/api/endpoints/test_organization_coding_agents.py:1084: in test_org_installation_403_returns_generic_failure_type
    response = self.get_success_response(self.organization.slug, method="post", **data)
src/sentry/testutils/cases.py:638: in get_success_response
    assert_status_code(response, 200, 300)
src/sentry/testutils/asserts.py:47: in assert_status_code
    assert minimum <= response.status_code < maximum, (
E   AssertionError: (500, b'{"detail":"Internal Error","errorId":null}')
E   assert 500 < 300
E    +  where 500 = <Response status_code=500, "application/json">.status_code
tests/sentry/integrations/api/endpoints/test_organization_coding_agents.py::OrganizationCodingAgentsPost403PermissionTest::test_non_403_error_returns_generic_failure_typelog
tests/sentry/integrations/api/endpoints/test_organization_coding_agents.py:1222: in test_non_403_error_returns_generic_failure_type
    response = self.get_success_response(self.organization.slug, method="post", **data)
src/sentry/testutils/cases.py:638: in get_success_response
    assert_status_code(response, 200, 300)
src/sentry/testutils/asserts.py:47: in assert_status_code
    assert minimum <= response.status_code < maximum, (
E   AssertionError: (500, b'{"detail":"Internal Error","errorId":null}')
E   assert 500 < 300
E    +  where 500 = <Response status_code=500, "application/json">.status_code

The "Failed to verify existence of branch" error from Cursor is
specifically a 400 response. Without checking the status code, errors
with other codes (e.g. 401, 500) containing that text would be
misclassified and skip more appropriate handlers.

Co-Authored-By: Claude <[email protected]>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 3, 2026

Backend Test Failures

Failures on decf5e6 in this run:

tests/sentry/integrations/api/endpoints/test_organization_coding_agents.py::OrganizationCodingAgentsPost403PermissionTest::test_org_installation_403_returns_generic_failure_typelog
tests/sentry/integrations/api/endpoints/test_organization_coding_agents.py:1084: in test_org_installation_403_returns_generic_failure_type
    response = self.get_success_response(self.organization.slug, method="post", **data)
src/sentry/testutils/cases.py:638: in get_success_response
    assert_status_code(response, 200, 300)
src/sentry/testutils/asserts.py:47: in assert_status_code
    assert minimum <= response.status_code < maximum, (
E   AssertionError: (500, b'{"detail":"Internal Error","errorId":null}')
E   assert 500 < 300
E    +  where 500 = <Response status_code=500, "application/json">.status_code
tests/sentry/integrations/api/endpoints/test_organization_coding_agents.py::OrganizationCodingAgentsPost403PermissionTest::test_non_403_error_returns_generic_failure_typelog
tests/sentry/integrations/api/endpoints/test_organization_coding_agents.py:1222: in test_non_403_error_returns_generic_failure_type
    response = self.get_success_response(self.organization.slug, method="post", **data)
src/sentry/testutils/cases.py:638: in get_success_response
    assert_status_code(response, 200, 300)
src/sentry/testutils/asserts.py:47: in assert_status_code
    assert minimum <= response.status_code < maximum, (
E   AssertionError: (500, b'{"detail":"Internal Error","errorId":null}')
E   assert 500 < 300
E    +  where 500 = <Response status_code=500, "application/json">.status_code

…check

Use getattr chain to safely access installation.model.provider, avoiding
AttributeError when the model attribute is None.

Co-Authored-By: Claude <[email protected]>
Replace defensive getattr chain with isinstance(installation,
CursorAgentIntegration) which is more idiomatic, avoids hard-coding
the "cursor" string, and implicitly handles the None case.

Co-Authored-By: Claude <[email protected]>
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.

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Replace string comparison `integration.provider == "cursor"` with
`isinstance(installation, CursorAgentIntegration)` to match the
pattern already used in coding_agent.py and cursor/webhooks/handler.py.

Co-Authored-By: Claude <[email protected]>
@isaacwang-sentry isaacwang-sentry merged commit 1e9cd45 into master Mar 4, 2026
76 checks passed
@isaacwang-sentry isaacwang-sentry deleted the iwang/cursor-github-access-backend branch March 4, 2026 18:10
isaacwang-sentry added a commit that referenced this pull request Mar 5, 2026
…#109695)

## Summary
- Adds a new `AutofixCursorGithubAccessModal` component that guides
users to install the Cursor GitHub App when Cursor can't access a
repository
- Updates both `useAutofix` and `useExplorerAutofix` hooks to detect the
new `cursor_github_access` failure type and show the modal instead of a
generic error toast

Depends on backend PR: #109692

AIML-2569

## Test plan
- [x] Frontend test passes: `CI=true pnpm test
static/app/components/events/autofix/autofixCursorGithubAccessModal.spec.tsx`
- [x] Modal renders with correct title, body, and link to
`https://github.com/apps/cursor`
- [x] When backend returns `cursor_github_access` failure_type, modal is
shown instead of generic error toast
JonasBa pushed a commit that referenced this pull request Mar 5, 2026
…#109695)

## Summary
- Adds a new `AutofixCursorGithubAccessModal` component that guides
users to install the Cursor GitHub App when Cursor can't access a
repository
- Updates both `useAutofix` and `useExplorerAutofix` hooks to detect the
new `cursor_github_access` failure type and show the modal instead of a
generic error toast

Depends on backend PR: #109692

AIML-2569

## Test plan
- [x] Frontend test passes: `CI=true pnpm test
static/app/components/events/autofix/autofixCursorGithubAccessModal.spec.tsx`
- [x] Modal renders with correct title, body, and link to
`https://github.com/apps/cursor`
- [x] When backend returns `cursor_github_access` failure_type, modal is
shown instead of generic error toast
@github-actions github-actions bot locked and limited conversation to collaborators Mar 20, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

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