Skip to content

Commit d8a010e

Browse files
committed
fix(github): propagate errors instead of masking as empty lists
get_repos: - Raise RuntimeError on API errors instead of returning [] - Re-raise network/parse exceptions after logging - Allows route handler to return proper 4xx/5xx get_all_repos: - Remove 'if not repos: break' which conflated errors with empty pages - Use 'len(repos) < per_page' to detect last page - Errors now bubble up instead of silently truncating results Fixes silent data loss bug where API failures returned HTTP 200 with partial results.
1 parent 650b40e commit d8a010e

1 file changed

Lines changed: 15 additions & 8 deletions

File tree

backend/services/github.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ async def get_repos(
9595
9696
Uses /user/repos which returns repos the user has explicit access to,
9797
including personal repos and org repos where user is a member
98+
99+
Raises exceptions on failure so callers can distinguish errors from empty results.
98100
"""
99101
try:
100102
async with httpx.AsyncClient() as client:
@@ -118,7 +120,7 @@ async def get_repos(
118120
status_code=response.status_code,
119121
page=page
120122
)
121-
return []
123+
raise RuntimeError(f"GitHub API error: {response.status_code}")
122124

123125
repos = []
124126
for repo in response.json():
@@ -144,36 +146,41 @@ async def get_repos(
144146
return repos
145147
except (httpx.RequestError, httpx.TimeoutException) as e:
146148
logger.error("Network error fetching GitHub repos", error=str(e), page=page)
147-
return []
149+
raise
148150
except (KeyError, ValueError, TypeError) as e:
149151
logger.error("Failed to parse GitHub repos response", error=str(e), page=page)
150-
return []
152+
raise
151153

152154
async def get_all_repos(
153155
self,
154156
include_forks: bool = False,
155-
max_pages: Optional[int] = 10
157+
max_pages: Optional[int] = 10,
158+
per_page: int = 100
156159
) -> list[GitHubRepo]:
157160
"""
158161
Fetch all repos with pagination
159162
160163
Args:
161164
include_forks: Whether to include forked repos
162165
max_pages: Maximum pages to fetch (None for no limit, default 10)
166+
per_page: Results per page (default 100)
167+
168+
Raises exceptions on failure - does not mask errors as empty results.
163169
"""
164170
all_repos = []
165171
page = 1
166172
while True:
167173
repos = await self.get_repos(
168174
include_forks=include_forks,
169-
per_page=100,
175+
per_page=per_page,
170176
page=page
171177
)
172-
if not repos:
173-
break
174178
all_repos.extend(repos)
175-
if len(repos) < 100:
179+
180+
# Stop when we get fewer results than requested (last page)
181+
if len(repos) < per_page:
176182
break
183+
177184
page += 1
178185

179186
if max_pages is not None and page > max_pages:

0 commit comments

Comments
 (0)