Skip to content

Commit 962bfa7

Browse files
Merge 816d525 into 2d3faf3
2 parents 2d3faf3 + 816d525 commit 962bfa7

File tree

4 files changed

+99
-16
lines changed

4 files changed

+99
-16
lines changed

src/sentry/seer/autofix/coding_agent.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,14 @@ def _launch_agents_for_repos(
332332
github_installation_id = sentry_integration.external_id
333333
except Exception:
334334
sentry_sdk.capture_exception(level="warning")
335+
elif (
336+
installation is not None
337+
and installation.model.provider == "cursor"
338+
and e.text
339+
and "Failed to verify existence of branch" in e.text
340+
):
341+
failure_type = "cursor_github_access"
342+
error_message = "Cursor does not have GitHub access to this repository. Please install the Cursor GitHub App to grant access."
335343
elif e.code == 401:
336344
error_message = f"Failed to make request to coding agent{url_part}. Please check that your API credentials are correct: {e.code} Error: {e.text}"
337345
else:

src/sentry/seer/explorer/coding_agent_handoff.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -145,22 +145,31 @@ def launch_coding_agents(
145145
failure_type = "generic"
146146
error_message = "Failed to launch coding agent"
147147
github_installation_id: str | None = None
148-
if isinstance(e, ApiError) and e.code == 403 and is_github_copilot:
149-
if e.text and "not licensed" in e.text.lower():
150-
failure_type = "github_copilot_not_licensed"
151-
error_message = "Your GitHub account does not have an active Copilot license. Please check your GitHub Copilot subscription."
152-
else:
153-
failure_type = "github_app_permissions"
154-
error_message = f"The Sentry GitHub App installation does not have the required permissions for {repo_name}. Please update your GitHub App permissions to include 'contents:write'."
155-
try:
156-
github_integrations = integration_service.get_integrations(
157-
organization_id=organization.id,
158-
providers=["github"],
159-
)
160-
if github_integrations:
161-
github_installation_id = github_integrations[0].external_id
162-
except Exception:
163-
sentry_sdk.capture_exception(level="warning")
148+
if isinstance(e, ApiError):
149+
if e.code == 403 and is_github_copilot:
150+
if e.text and "not licensed" in e.text.lower():
151+
failure_type = "github_copilot_not_licensed"
152+
error_message = "Your GitHub account does not have an active Copilot license. Please check your GitHub Copilot subscription."
153+
else:
154+
failure_type = "github_app_permissions"
155+
error_message = f"The Sentry GitHub App installation does not have the required permissions for {repo_name}. Please update your GitHub App permissions to include 'contents:write'."
156+
try:
157+
github_integrations = integration_service.get_integrations(
158+
organization_id=organization.id,
159+
providers=["github"],
160+
)
161+
if github_integrations:
162+
github_installation_id = github_integrations[0].external_id
163+
except Exception:
164+
sentry_sdk.capture_exception(level="warning")
165+
elif (
166+
integration is not None
167+
and integration.provider == "cursor"
168+
and e.text
169+
and "Failed to verify existence of branch" in e.text
170+
):
171+
failure_type = "cursor_github_access"
172+
error_message = "Cursor does not have GitHub access to this repository. Please install the Cursor GitHub App to grant access."
164173

165174
failure: dict = {
166175
"repo_name": repo_name,

tests/sentry/seer/autofix/test_coding_agent.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,37 @@ def test_api_error_401_includes_credentials_message(
281281
assert "401" in error_message
282282
assert '{"code":"internal","message":"Error"}' in error_message
283283

284+
@patch("sentry.seer.autofix.coding_agent.store_coding_agent_states_to_seer")
285+
@patch("sentry.seer.autofix.coding_agent.get_coding_agent_prompt")
286+
@patch("sentry.seer.autofix.coding_agent.get_project_seer_preferences")
287+
def test_verify_branch_error_returns_cursor_github_access_failure_type(
288+
self, mock_get_preferences, mock_get_prompt, mock_store_states
289+
):
290+
"""Test that a 400 ApiError with 'Failed to verify existence of branch' returns cursor_github_access failure_type."""
291+
mock_get_preferences.side_effect = SeerApiError("API Error", 500)
292+
mock_get_prompt.return_value = "Test prompt"
293+
294+
mock_installation = MagicMock()
295+
mock_installation.model.provider = "cursor"
296+
mock_installation.launch.side_effect = ApiError(
297+
text='{"error":"Failed to verify existence of branch \'main\' in repository owner/repo. Please ensure the branch name is correct."}',
298+
code=400,
299+
)
300+
301+
result = _launch_agents_for_repos(
302+
installation=mock_installation,
303+
autofix_state=self.autofix_state,
304+
run_id=self.run_id,
305+
organization=self.organization,
306+
trigger_source=AutofixTriggerSource.SOLUTION,
307+
)
308+
309+
assert len(result["failures"]) == 1
310+
failure = result["failures"][0]
311+
assert failure["failure_type"] == "cursor_github_access"
312+
assert "Cursor does not have GitHub access" in failure["error_message"]
313+
assert "install the Cursor GitHub App" in failure["error_message"]
314+
284315
@patch("sentry.seer.autofix.coding_agent.store_coding_agent_states_to_seer")
285316
@patch("sentry.seer.autofix.coding_agent.get_coding_agent_prompt")
286317
@patch("sentry.seer.autofix.coding_agent.get_project_seer_preferences")

tests/sentry/seer/explorer/test_coding_agent_handoff.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,38 @@ def test_copilot_not_licensed_403_returns_github_copilot_not_licensed_failure_ty
152152
failure = result["failures"][0]
153153
assert failure["failure_type"] == "github_copilot_not_licensed"
154154
assert "Copilot license" in failure["error_message"]
155+
156+
@patch("sentry.seer.explorer.coding_agent_handoff.store_coding_agent_states_to_seer")
157+
@patch("sentry.seer.explorer.coding_agent_handoff._validate_and_get_integration")
158+
def test_verify_branch_error_returns_cursor_github_access_failure_type(
159+
self, mock_validate, mock_store
160+
):
161+
"""Test that a 400 ApiError with 'Failed to verify existence of branch' returns cursor_github_access failure_type.
162+
163+
When Cursor returns a 400 with this error, the Cursor GitHub App hasn't been
164+
granted access to the target repository. We should show the Cursor GitHub
165+
access modal instead of a generic error.
166+
"""
167+
mock_integration = MagicMock()
168+
mock_integration.provider = "cursor"
169+
mock_installation = MagicMock()
170+
mock_installation.launch.side_effect = ApiError(
171+
text='{"error":"Failed to verify existence of branch \'main\' in repository owner/repo. Please ensure the branch name is correct."}',
172+
code=400,
173+
)
174+
mock_validate.return_value = (mock_integration, mock_installation)
175+
176+
result = launch_coding_agents(
177+
organization=self.organization,
178+
integration_id=1,
179+
run_id=self.run_id,
180+
prompt="Fix the bug",
181+
repos=["owner/repo"],
182+
)
183+
184+
assert len(result["successes"]) == 0
185+
assert len(result["failures"]) == 1
186+
failure = result["failures"][0]
187+
assert failure["failure_type"] == "cursor_github_access"
188+
assert "Cursor does not have GitHub access" in failure["error_message"]
189+
assert "install the Cursor GitHub App" in failure["error_message"]

0 commit comments

Comments
 (0)