{"record":{"id":"1a8cd0c5a40899ac","repo":"ruvnet/ruflo","slug":"rate-limit-1a8cd0","errorCode":"RATE_LIMIT","errorMessage":"${message}","messagePattern":"\\$\\{message\\}","errorType":"exception","errorClass":"RateLimitError","httpStatus":429,"severity":"error","filePath":"v3/@claude-flow/providers/src/google-provider.ts","lineNumber":417,"sourceCode":"\n  private async handleErrorResponse(response: Response): Promise<never> {\n    const errorText = await response.text();\n    let errorData: { error?: { message?: string } };\n\n    try {\n      errorData = JSON.parse(errorText);\n    } catch {\n      errorData = { error: { message: errorText } };\n    }\n\n    const message = errorData.error?.message || 'Unknown error';\n\n    switch (response.status) {\n      case 401:\n      case 403:\n        throw new AuthenticationError(message, 'google', errorData);\n      case 429:\n        throw new RateLimitError(message, 'google', undefined, errorData);\n      default:\n        throw new LLMProviderError(\n          message,\n          `GOOGLE_${response.status}`,\n          'google',\n          response.status,\n          response.status >= 500,\n          errorData\n        );\n    }\n  }\n}\n","sourceCodeStart":399,"sourceCodeEnd":430,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/providers/src/google-provider.ts#L399-L430","documentation":"GoogleProvider maps HTTP 429 to RateLimitError (retryable, statusCode 429, retryAfter undefined - Gemini's retry-after header is not parsed here). Google enforces per-project, per-model RPM/TPM quotas, with shared no-cost tier limits that are noticeably tight on popular Gemini models.","triggerScenarios":"A burst of complete()/streamComplete() calls on a Gemini model exceeding the per-minute request or token quota for the project, especially on the free tier.","commonSituations":"Fan-out generation loops without throttling; polling or load-test traffic; free-tier quota shared across dev and prod because both use the same project key.","solutions":["Retry on RateLimitError with exponential backoff plus jitter; do not retry immediately","Throttle concurrency client-side (p-limit, queue) so sustained RPM stays under the quota","Move to a paid/billed project or request a quota increase if traffic legitimately exceeds the tier","Spread load across models - quotas are per model, so alternating gemini variants can help"],"exampleFix":"// before\nconst results = await Promise.all(prompts.map(p => provider.complete({ prompt: p }))); // burst -> 429\n\n// after - bounded concurrency + backoff on RateLimitError\nconst limit = pLimit(2);\nconst results = await Promise.all(prompts.map(p => limit(() =>\n  withBackoff(() => provider.complete({ prompt: p }), { on: RateLimitError })\n)));","handlingStrategy":"retry","validationCode":null,"typeGuard":"import { RateLimitError } from './types.js';\nfunction isGoogleRateLimit(e: unknown): e is RateLimitError {\n  return e instanceof RateLimitError && e.provider === 'google';\n}","tryCatchPattern":"for (let attempt = 0; ; attempt++) {\n  try {\n    return await provider.complete(req);\n  } catch (e) {\n    if (isGoogleRateLimit(e) && attempt < 5) {\n      // retryAfter is undefined in this mapping - use own backoff\n      await new Promise(r => setTimeout(r, Math.min(60_000, 2 ** attempt * 1000) + Math.random() * 500));\n      continue;\n    }\n    throw e;\n  }\n}","preventionTips":["Throttle to a sustained RPM below the project quota; per-model quotas mean spreading across models helps","Cache identical completions (the ProviderManager supports request caching)","Separate dev and prod projects so free-tier quotas are not shared"],"tags":["rate-limit","google","gemini","http-429","quota","retry"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}