Skip to content

Commit 37fd3a1

Browse files
docs-botCopilot
andauthored
Retry code search API rate limits in github/github link check (#64410)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d5ba06c commit 37fd3a1

2 files changed

Lines changed: 26 additions & 2 deletions

File tree

‎src/workflows/secondary-ratelimit-retry.ts‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export async function octoSecondaryRatelimitRetry<T>(
2020
} catch (error) {
2121
if (
2222
(isRequestError(error, 403) || isRequestError(error, 429)) &&
23-
/You have exceeded a secondary rate limit/.test(error.message)
23+
/You have exceeded a secondary rate limit|API rate limit exceeded/.test(error.message)
2424
) {
2525
const retryAfterMs = getRetryAfterMs(error)
2626
if (retryAfterMs !== null && retryAfterMs > MAX_RETRY_AFTER_MS) {
@@ -46,7 +46,14 @@ export async function octoSecondaryRatelimitRetry<T>(
4646

4747
function getRetryAfterMs(error: object): number | null {
4848
const headers = (error as { response?: { headers?: Record<string, unknown> } }).response?.headers
49-
const value = headers?.['retry-after']
49+
let value = headers?.['retry-after']
50+
if (value === undefined) {
51+
// The code search API's 30 requests/minute limit only sends a reset time.
52+
const reset = Number(headers?.['x-ratelimit-reset'])
53+
if (Number.isFinite(reset) && reset > 0) {
54+
value = String(Math.max(0, Math.ceil(reset - Date.now() / 1000)) + 1)
55+
}
56+
}
5057
if (typeof value !== 'string' && typeof value !== 'number') return null
5158
const seconds = Number(value)
5259
if (String(value).trim() === '' || !Number.isFinite(seconds) || seconds < 0) return null

‎src/workflows/tests/secondary-ratelimit-retry.ts‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,23 @@ describe('octoSecondaryRatelimitRetry', () => {
3232
expect(fn).toHaveBeenCalledTimes(2)
3333
})
3434

35+
test('retries a search API rate limit using x-ratelimit-reset', async () => {
36+
vi.useFakeTimers()
37+
vi.setSystemTime(new Date('2026-01-01T00:00:00Z'))
38+
const reset = String(Date.parse('2026-01-01T00:00:10Z') / 1000)
39+
const fn = failOnceThen(
40+
makeError(403, 'API rate limit exceeded for user ID 1.', { 'x-ratelimit-reset': reset }),
41+
'ok',
42+
)
43+
const promise = octoSecondaryRatelimitRetry(fn, { attempts: 2, sleepTime: 0 })
44+
45+
await vi.advanceTimersByTimeAsync(10_999)
46+
expect(fn).toHaveBeenCalledTimes(1)
47+
await vi.advanceTimersByTimeAsync(1)
48+
expect(fn).toHaveBeenCalledTimes(2)
49+
await expect(promise).resolves.toBe('ok')
50+
})
51+
3552
test('does not retry a 429 without the secondary rate limit message', async () => {
3653
const error = makeError(429, 'Too Many Requests')
3754
const fn = vi.fn().mockRejectedValue(error)

0 commit comments

Comments
 (0)