{"record":{"id":"300c5bfc582ede97","repo":"yikart/AiToEarn","slug":"too-many-requests","errorCode":"TOO_MANY_REQUESTS","errorMessage":"Too many requests","messagePattern":"Too many requests","errorType":"http","errorClass":"HttpException","httpStatus":429,"severity":"warning","filePath":"project/aitoearn-backend/apps/aitoearn-server/src/common/guards/rate-limit.guard.ts","lineNumber":83,"sourceCode":"\n    // 生成限流键\n    const key = keyGenerator\n      ? keyGenerator(request)\n      : this.getDefaultKey(request)\n\n    try {\n      const count = await this.redisService.incrementRateLimit(key, ttl)\n\n      // 设置响应头\n      const response = context.switchToHttp().getResponse()\n      response.setHeader('X-RateLimit-Limit', limit.toString())\n      response.setHeader('X-RateLimit-Reset', (Date.now() + ttl * 1000).toString())\n\n      // 检查是否超过限制\n      if (count > limit) {\n        response.setHeader('X-RateLimit-Remaining', '0')\n        this.logger.warn(`Rate limit exceeded for key: ${key}, count: ${count}, limit: ${limit}`)\n        throw new HttpException(\n          {\n            code: HttpStatus.TOO_MANY_REQUESTS,\n            message: 'Too many requests',\n            data: { ttl },\n          },\n          HttpStatus.TOO_MANY_REQUESTS,\n        )\n      }\n\n      response.setHeader('X-RateLimit-Remaining', (limit - count).toString())\n\n      return true\n    }\n    catch (error) {\n      if (error instanceof HttpException) {\n        throw error\n      }\n      this.logger.fatal(error, `Rate limit check failed`)","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/yikart/AiToEarn/blob/d3aa8bea5b146a8675607cf0144d891aad3e9683/project/aitoearn-backend/apps/aitoearn-server/src/common/guards/rate-limit.guard.ts#L65-L101","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nsetInterval(callApi, 100) // hammers the limit\n// after\nawait pRetry(callApi, { retries: 5, minTimeout: 1000, factor: 2, onFailedAttempt: e => { if (e.statusCode !== 429) throw e } })","handlingStrategy":"retry","validationCode":"// Check remaining quota from the previous response before calling again\nconst remaining = Number(prevResponse.headers['x-ratelimit-remaining'])\nconst resetAt = Number(prevResponse.headers['x-ratelimit-reset'])\nif (remaining <= 0 && Date.now() < resetAt) await sleep(resetAt - Date.now())","typeGuard":"function isRateLimitError(e: unknown): e is { status: 429; response: { data: { ttl: number } } } {\n  return typeof e === 'object' && e !== null && 'status' in e && (e as any).status === 429\n}","tryCatchPattern":"try {\n  res = await api.get(url)\n} catch (e) {\n  if (e.status === 429) {\n    const waitMs = (e.response?.data?.ttl ?? 1) * 1000\n    await sleep(waitMs)\n    res = await api.get(url) // single retry after window; otherwise use exponential backoff\n  } else throw e\n}","preventionTips":["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."],"tags":["rate-limit","http-429","throttling"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"d3aa8bea5b146a8675607cf0144d891aad3e9683","analyzedAt":"2026-08-31T14:19:24.185Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}