-
Notifications
You must be signed in to change notification settings - Fork 325
fix: handle Ask GitHub rate limits #1476
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,3 +29,21 @@ export const isUnauthorized = (err: unknown): boolean => getStatus(err) === 401; | |
| export const isForbidden = (err: unknown): boolean => getStatus(err) === 403; | ||
| export const isNotFound = (err: unknown): boolean => getStatus(err) === 404; | ||
| export const isGone = (err: unknown): boolean => getStatus(err) === 410; | ||
|
|
||
| export const isGitHubRateLimitError = (err: unknown): boolean => { | ||
| const status = getStatus(err); | ||
| if (status !== 403 && status !== 429) { | ||
| return false; | ||
| } | ||
|
|
||
| if (status === 429) { | ||
| return true; | ||
| } | ||
|
|
||
| const responseHeaders = (err as { response?: { headers?: Record<string, string | undefined> } }).response?.headers; | ||
| const message = (err as { message?: unknown }).message; | ||
|
|
||
| return responseHeaders?.['x-ratelimit-remaining'] === '0' | ||
| || responseHeaders?.['retry-after'] !== undefined | ||
| || (typeof message === 'string' && /rate limit/i.test(message)); | ||
|
Comment on lines
+43
to
+48
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Handle wrapped errors consistently with
🤖 Prompt for AI Agents |
||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| import { cleanup, fireEvent, render, screen } from '@testing-library/react'; | ||
| import { afterEach, describe, expect, test, vi } from 'vitest'; | ||
|
|
||
| const mocks = vi.hoisted(() => ({ | ||
| refresh: vi.fn(), | ||
| })); | ||
|
|
||
| vi.mock('next/navigation', () => ({ | ||
| useRouter: () => ({ | ||
| refresh: mocks.refresh, | ||
| }), | ||
| })); | ||
|
|
||
| const { GitHubRateLimitExceeded } = await import('./githubRateLimitExceeded'); | ||
|
|
||
| afterEach(() => { | ||
| cleanup(); | ||
| vi.clearAllMocks(); | ||
| }); | ||
|
|
||
| describe('GitHubRateLimitExceeded', () => { | ||
| test('offers to retry the request', () => { | ||
| render(<GitHubRateLimitExceeded />); | ||
|
|
||
| expect(screen.getByText('GitHub is temporarily busy')).toBeTruthy(); | ||
| fireEvent.click(screen.getByRole('button', { name: 'Try again' })); | ||
|
|
||
| expect(mocks.refresh).toHaveBeenCalledOnce(); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| 'use client'; | ||
|
|
||
| import { Button } from "@/components/ui/button"; | ||
| import { Clock3 } from "lucide-react"; | ||
| import { useRouter } from "next/navigation"; | ||
|
|
||
| export function GitHubRateLimitExceeded() { | ||
| const router = useRouter(); | ||
|
|
||
| return ( | ||
| <div className="flex flex-col items-center justify-center min-h-screen p-4"> | ||
| <Clock3 className="w-12 h-12 text-muted-foreground mb-4" /> | ||
| <h2 className="text-2xl font-semibold mb-2"> | ||
| GitHub is temporarily busy | ||
| </h2> | ||
| <p className="text-muted-foreground text-center max-w-md"> | ||
| GitHub is receiving too many requests right now. Please wait a few minutes, then try again. | ||
| </p> | ||
| <Button | ||
| className="mt-6" | ||
| variant="outline" | ||
| onClick={() => router.refresh()} | ||
| > | ||
| Try again | ||
| </Button> | ||
| </div> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: sourcebot-dev/sourcebot
Length of output: 30017
🏁 Script executed:
Repository: sourcebot-dev/sourcebot
Length of output: 10840
🏁 Script executed:
Repository: sourcebot-dev/sourcebot
Length of output: 5228
🏁 Script executed:
Repository: sourcebot-dev/sourcebot
Length of output: 9447
Require auth before the GitHub lookup endpoint.
/api/experimental/add-github-repois open, and the UI’swithOptionalAuthguard doesn’t protect the backend route. Any client that can reach the worker can probe arbitrary owner/repo pairs through the GitHub token and persist/index the result. If this must stay public, use a read-only public token and add abuse throttling.🤖 Prompt for AI Agents