ruvnet/ruflo · error · RateLimitError
RATE_LIMIT
RATE_LIMIT
Error message
${message} What it means
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.
Source
Thrown at v3/@claude-flow/providers/src/openai-provider.ts:471
private async handleErrorResponse(response: Response): Promise<never> {
const errorText = await response.text();
let errorData: { error?: { message?: string } };
try {
errorData = JSON.parse(errorText);
} catch {
errorData = { error: { message: errorText } };
}
const message = errorData.error?.message || 'Unknown error';
switch (response.status) {
case 401:
throw new AuthenticationError(message, 'openai', errorData);
case 429:
const retryAfter = response.headers.get('retry-after');
throw new RateLimitError(
message,
'openai',
retryAfter ? parseInt(retryAfter) : undefined,
errorData
);
case 404:
throw new ModelNotFoundError(this.config.model, 'openai', errorData);
default:
throw new LLMProviderError(
message,
`OPENAI_${response.status}`,
'openai',
response.status,
response.status >= 500,
errorData
);
}
}View on GitHub (pinned to fa13ee4ad6)
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
Example fix
// before
const res = await provider.complete(req); // 429 propagates immediately
// after - respect retryAfter from the RateLimitError
catch (e) {
if (e instanceof RateLimitError) {
const wait = (e.retryAfter ?? 2 ** attempt) * 1000;
await new Promise(r => setTimeout(r, wait));
return provider.complete(req);
}
throw e;
} Defensive patterns
Strategy: retry
Type guard
import { RateLimitError } from './types.js';
function isOpenAIRateLimit(e: unknown): e is RateLimitError {
return e instanceof RateLimitError && e.provider === 'openai';
} Try / catch
for (let attempt = 0; ; attempt++) {
try {
return await provider.complete(req);
} catch (e) {
if (isOpenAIRateLimit(e) && attempt < 5) {
const waitMs = (e.retryAfter ?? 2 ** attempt) * 1000; // retryAfter parsed from header
await new Promise(r => setTimeout(r, waitMs + Math.random() * 500));
continue;
}
throw e;
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: Fan-out generation without throttling; batch jobs sharing one org across services; sudden traffic spike; tier limits too low for the workload.
Related errors
- RATE_LIMIT
- RATE_LIMIT
- OpenAI API error: ${response.status} - ${error}
- Amendment rate limit exceeded: ${this.maxAmendmentsPerWindow
- Provider ${provider.id} is rate limited
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/78976701e0eb62e5.
Report an issue: GitHub.