-
-
Notifications
You must be signed in to change notification settings - Fork 926
Closed
Description
Summary
When the API returns 429 Too Many Requests, the Retry-After header is generate from the absolute Unix epoch reset value, which is non-standard. The resulting large values returned can cause some clients to choke. If they treat it as delay-seconds, a value like 1771404540 means wait ~56 years (!)
Standards reference (RFC)
- RFC 9110, Section 10.2.3 (
Retry-After):
https://www.rfc-editor.org/rfc/rfc9110#section-10.2.3 - RFC 6585, Section 4 (
429 Too Many Requests+Retry-After):
https://www.rfc-editor.org/rfc/rfc6585#section-4
Evidence
1) Server code sets Retry-After to absolute reset epoch
In convex/lib/httpRateLimit.ts:121-127:
const resetSeconds = Math.ceil(result.resetAt / 1000)
return {
'X-RateLimit-Limit': String(result.limit),
'X-RateLimit-Remaining': String(result.remaining),
'X-RateLimit-Reset': String(resetSeconds),
...(result.allowed ? {} : { 'Retry-After': String(resetSeconds) }),
}result.resetAt is milliseconds since epoch. So Retry-After is emitted as epoch seconds (e.g. 1771404540), not a delay.
2) Live API confirms X-RateLimit-Reset is epoch seconds
Sample response from GET /api/v1/download?slug=gifgrep on 2026-02-18:
HTTP/2 200
x-ratelimit-limit: 20
x-ratelimit-remaining: 18
x-ratelimit-reset: 1771404540 <--- PROBLEMATICExpected behavior
On 429:
Retry-Aftershould be a delay in seconds (ceil((resetAt - now)/1000)) or an HTTP-date.- Response should still include reset metadata (
X-RateLimit-Resetor standardizedRateLimit-*headers). - CLI should present actionable wait info to users.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels