yikart/AiToEarn · warning · HttpException
TOO_MANY_REQUESTS
TOO_MANY_REQUESTS
Error message
Too many requests
What it means
The global rate-limit guard in aitoearn-server counts requests per key against a configured limit within a ttl window. When count exceeds limit it throws an HttpException with code TOO_MANY_REQUESTS and includes the window ttl in data; X-RateLimit-Remaining: 0 and X-RateLimit-Reset headers are set on the response.
Source
Thrown at project/aitoearn-backend/apps/aitoearn-server/src/common/guards/rate-limit.guard.ts:83
// 生成限流键
const key = keyGenerator
? keyGenerator(request)
: this.getDefaultKey(request)
try {
const count = await this.redisService.incrementRateLimit(key, ttl)
// 设置响应头
const response = context.switchToHttp().getResponse()
response.setHeader('X-RateLimit-Limit', limit.toString())
response.setHeader('X-RateLimit-Reset', (Date.now() + ttl * 1000).toString())
// 检查是否超过限制
if (count > limit) {
response.setHeader('X-RateLimit-Remaining', '0')
this.logger.warn(`Rate limit exceeded for key: ${key}, count: ${count}, limit: ${limit}`)
throw new HttpException(
{
code: HttpStatus.TOO_MANY_REQUESTS,
message: 'Too many requests',
data: { ttl },
},
HttpStatus.TOO_MANY_REQUESTS,
)
}
response.setHeader('X-RateLimit-Remaining', (limit - count).toString())
return true
}
catch (error) {
if (error instanceof HttpException) {
throw error
}
this.logger.fatal(error, `Rate limit check failed`)View on GitHub (pinned to d3aa8bea5b)
Solutions
- Wait ttl seconds (from data.ttl or the X-RateLimit-Reset header) before retrying.
- Implement exponential backoff with jitter on 429 responses.
- Cache responses / reduce call frequency or batch requests.
- If limits are genuinely too low for legitimate traffic, raise the limit configuration for the route or use a higher-tier key.
Example fix
// before
setInterval(callApi, 100) // hammers the limit
// after
await pRetry(callApi, { retries: 5, minTimeout: 1000, factor: 2, onFailedAttempt: e => { if (e.statusCode !== 429) throw e } }) Defensive patterns
Strategy: retry
Validate before calling
// Check remaining quota from the previous response before calling again const remaining = Number(prevResponse.headers['x-ratelimit-remaining']) const resetAt = Number(prevResponse.headers['x-ratelimit-reset']) if (remaining <= 0 && Date.now() < resetAt) await sleep(resetAt - Date.now())
Type guard
function isRateLimitError(e: unknown): e is { status: 429; response: { data: { ttl: number } } } {
return typeof e === 'object' && e !== null && 'status' in e && (e as any).status === 429
} Try / catch
try {
res = await api.get(url)
} catch (e) {
if (e.status === 429) {
const waitMs = (e.response?.data?.ttl ?? 1) * 1000
await sleep(waitMs)
res = await api.get(url) // single retry after window; otherwise use exponential backoff
} else throw e
} Prevention
- Honor X-RateLimit-Remaining / X-RateLimit-Reset headers.
- Use exponential backoff with jitter on 429.
- Debounce and deduplicate client requests.
- Avoid sharing one API key across many concurrent clients.
When it happens
Trigger: Any HTTP endpoint called more often than the configured limit within the ttl window for the same key — polling loops, retries without backoff, or many clients sharing one IP/API key and exhausting the bucket.
Common situations: Load tests or scripts hammering the API; multiple users behind one NAT/proxy sharing the rate-limit key; frontend retry loops after failures; misconfigured (too low) per-route limits.
Understand the failure class
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Related errors
- ChannelPlatformApiFailed
- 获取Twitter时间线失败: ${error.response?.data?.error || error.messa
- 发布推文失败: ${error.response?.data?.error || error.message}
- 获取推文统计数据失败: ${error.response?.data?.error || error.message}
- 搜索推文失败: ${error.response?.data?.error || error.message}
AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31).
Data as JSON: /api/errors/300c5bfc582ede97.
Report an issue: GitHub.