From 196e9e0fada38354fc762f7d2a0502579a6ec1e8 Mon Sep 17 00:00:00 2001 From: Hiroshi Nishio Date: Tue, 24 Feb 2026 13:03:22 -0800 Subject: [PATCH 1/3] Handle empty GitHub repos gracefully in get_email_from_commits Return None when GitHub API returns 409 (empty repo) instead of raising an HTTPError that gets reported to Sentry as a false alarm. --- services/github/users/get_email_from_commits.py | 5 +++++ services/github/users/test_get_email_from_commits.py | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/services/github/users/get_email_from_commits.py b/services/github/users/get_email_from_commits.py index 82ba5f21..dacb50cd 100644 --- a/services/github/users/get_email_from_commits.py +++ b/services/github/users/get_email_from_commits.py @@ -3,6 +3,7 @@ from config import GITHUB_API_URL, TIMEOUT from services.github.utils.create_headers import create_headers from utils.error.handle_exceptions import handle_exceptions +from utils.logging.logging_config import logger NOREPLY_SUFFIX = "@users.noreply.github.com" @@ -17,6 +18,10 @@ def get_email_from_commits(owner: str, repo: str, username: str, token: str): params={"author": username, "per_page": 5}, timeout=TIMEOUT, ) + if response.status_code == 409: + logger.info("Repository %s/%s is empty, skipping email lookup", owner, repo) + return None + response.raise_for_status() commits: list[dict[str, dict[str, dict[str, str]]]] = response.json() for commit in commits: diff --git a/services/github/users/test_get_email_from_commits.py b/services/github/users/test_get_email_from_commits.py index 60940199..b14a7bbb 100644 --- a/services/github/users/test_get_email_from_commits.py +++ b/services/github/users/test_get_email_from_commits.py @@ -48,6 +48,13 @@ def test_returns_none_when_no_commits(mock_get: MagicMock): assert result is None +@patch("services.github.users.get_email_from_commits.requests.get") +def test_returns_none_when_repo_is_empty(mock_get: MagicMock): + mock_get.return_value.status_code = 409 + result = get_email_from_commits(owner="o", repo="r", username="u", token="t") + assert result is None + + @patch("services.github.users.get_email_from_commits.requests.get") def test_returns_none_on_api_error(mock_get: MagicMock): mock_get.side_effect = Exception("API error") From e7d4843e20bbb4c536b5674773a78ab721fdabc0 Mon Sep 17 00:00:00 2001 From: Hiroshi Nishio Date: Tue, 24 Feb 2026 13:04:29 -0800 Subject: [PATCH 2/3] Remove .get() defaults, handle None explicitly --- services/github/users/get_email_from_commits.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/services/github/users/get_email_from_commits.py b/services/github/users/get_email_from_commits.py index dacb50cd..6b9e8800 100644 --- a/services/github/users/get_email_from_commits.py +++ b/services/github/users/get_email_from_commits.py @@ -25,9 +25,13 @@ def get_email_from_commits(owner: str, repo: str, username: str, token: str): response.raise_for_status() commits: list[dict[str, dict[str, dict[str, str]]]] = response.json() for commit in commits: - commit_obj = commit.get("commit", {}) - author = commit_obj.get("author", {}) - email = author.get("email", "") + commit_obj = commit.get("commit") + if not commit_obj: + continue + author = commit_obj.get("author") + if not author: + continue + email = author.get("email") if email and not email.endswith(NOREPLY_SUFFIX): return email return None From 9c8005ad040827c0231e961427a6050203c3699a Mon Sep 17 00:00:00 2001 From: Hiroshi Nishio Date: Tue, 24 Feb 2026 13:05:36 -0800 Subject: [PATCH 3/3] Guard against overwriting email with None from get_email_from_commits --- services/webhook/check_suite_handler.py | 4 +++- services/webhook/review_run_handler.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/services/webhook/check_suite_handler.py b/services/webhook/check_suite_handler.py index 5e73b1c7..77b9b70c 100644 --- a/services/webhook/check_suite_handler.py +++ b/services/webhook/check_suite_handler.py @@ -177,9 +177,11 @@ async def handle_check_suite( sender_name = payload["sender"]["login"] sender_info = get_user_public_info(username=sender_name, token=token) if not sender_info.email: - sender_info.email = get_email_from_commits( + email = get_email_from_commits( owner=owner_name, repo=repo_name, username=sender_name, token=token ) + if email: + sender_info.email = email # Extract PR related variables and return if no PR is associated with this check suite pull_requests = check_suite["pull_requests"] diff --git a/services/webhook/review_run_handler.py b/services/webhook/review_run_handler.py index 5e6ec374..7549faa8 100644 --- a/services/webhook/review_run_handler.py +++ b/services/webhook/review_run_handler.py @@ -110,9 +110,11 @@ async def handle_review_run( token = get_installation_access_token(installation_id=installation_id) sender_info = get_user_public_info(username=sender_name, token=token) if not sender_info.email: - sender_info.email = get_email_from_commits( + email = get_email_from_commits( owner=owner_name, repo=repo_name, username=sender_name, token=token ) + if email: + sender_info.email = email # Get all comments in the review thread thread_comments = get_review_thread_comments(