{"record":{"id":"78976701e0eb62e5","repo":"ruvnet/ruflo","slug":"rate-limit-789767","errorCode":"RATE_LIMIT","errorMessage":"${message}","messagePattern":"\\$\\{message\\}","errorType":"exception","errorClass":"RateLimitError","httpStatus":429,"severity":"error","filePath":"v3/@claude-flow/providers/src/openai-provider.ts","lineNumber":471,"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        throw new AuthenticationError(message, 'openai', errorData);\n      case 429:\n        const retryAfter = response.headers.get('retry-after');\n        throw new RateLimitError(\n          message,\n          'openai',\n          retryAfter ? parseInt(retryAfter) : undefined,\n          errorData\n        );\n      case 404:\n        throw new ModelNotFoundError(this.config.model, 'openai', errorData);\n      default:\n        throw new LLMProviderError(\n          message,\n          `OPENAI_${response.status}`,\n          'openai',\n          response.status,\n          response.status >= 500,\n          errorData\n        );\n    }\n  }","sourceCodeStart":453,"sourceCodeEnd":489,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/providers/src/openai-provider.ts#L453-L489","documentation":"OpenAIProvider maps HTTP 429 to RateLimitError (retryable, statusCode 429) and is the one mapping that parses the response's retry-after header into error.retryAfter (seconds). OpenAI returns 429 when org/project rate limits (RPM, TPM, IPU) or quota is exceeded.","triggerScenarios":"Bursts of complete()/streamComplete() exceeding the org's requests-per-minute or tokens-per-minute limit; hitting a monthly billing quota; too many parallel requests from one key.","commonSituations":"Fan-out generation without throttling; batch jobs sharing one org across services; sudden traffic spike; tier limits too low for the workload.","solutions":["Honor error.retryAfter when present - sleep that many seconds before the next attempt","Otherwise back off exponentially with jitter, and cap concurrent in-flight requests","Enable the ProviderManager request cache so identical prompts do not re-bill and re-count","Check usage and limits in the OpenAI dashboard; request an increase or upgrade the tier if sustained"],"exampleFix":"// before\nconst res = await provider.complete(req); // 429 propagates immediately\n\n// after - respect retryAfter from the RateLimitError\ncatch (e) {\n  if (e instanceof RateLimitError) {\n    const wait = (e.retryAfter ?? 2 ** attempt) * 1000;\n    await new Promise(r => setTimeout(r, wait));\n    return provider.complete(req);\n  }\n  throw e;\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":"import { RateLimitError } from './types.js';\nfunction isOpenAIRateLimit(e: unknown): e is RateLimitError {\n  return e instanceof RateLimitError && e.provider === 'openai';\n}","tryCatchPattern":"for (let attempt = 0; ; attempt++) {\n  try {\n    return await provider.complete(req);\n  } catch (e) {\n    if (isOpenAIRateLimit(e) && attempt < 5) {\n      const waitMs = (e.retryAfter ?? 2 ** attempt) * 1000; // retryAfter parsed from header\n      await new Promise(r => setTimeout(r, waitMs + Math.random() * 500));\n      continue;\n    }\n    throw e;\n  }\n}","preventionTips":["Honor error.retryAfter when present instead of guessing the delay","Keep client-side concurrency under the org's RPM and cache identical prompts","Monitor token usage per minute to back off before the 429 arrives"],"tags":["rate-limit","openai","http-429","throttling","retry","quota"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}