ruvnet/ruflo · warning
Provider ${provider.id} is rate limited
Error message
Provider ${provider.id} is rate limited What it means
ProviderAdapter's per-provider rate-limit guard throws when a provider's status is 'rate-limited' and the 60-second window (rateLimits.resetAt) has not elapsed. Requests are refused before dispatch to protect the provider instead of being sent upstream to fail.
Source
Thrown at v3/@claude-flow/integration/src/provider-adapter.ts:926
}
/**
* Check rate limits before request
*/
private checkRateLimits(provider: Provider): void {
// Reset rate limits if needed
const now = Date.now();
if (now >= provider.rateLimits.resetAt) {
provider.rateLimits.currentRequests = 0;
provider.rateLimits.currentTokens = 0;
provider.rateLimits.resetAt = now + 60000;
if (provider.status === 'rate-limited') {
provider.status = 'available';
}
}
if (provider.status === 'rate-limited') {
throw new Error(`Provider ${provider.id} is rate limited`);
}
}
/**
* Check cost limits
*/
private checkCostLimits(): void {
const now = Date.now();
// Reset hourly cost if needed
if (now >= this.hourlyCost.resetAt) {
this.hourlyCost.amount = 0;
this.hourlyCost.resetAt = now + 3600000;
}
if (this.hourlyCost.amount >= this.config.costLimitPerHour!) {
throw new Error(
`Hourly cost limit exceeded: $${this.hourlyCost.amount.toFixed(2)} / $${this.config.costLimitPerHour}`View on GitHub (pinned to fa13ee4ad6)
Solutions
- Back off until rateLimits.resetAt (at most 60s into the window) and resubmit
- Throttle or queue upstream (rate limiter with jitter) so bursts stay under the provider's configured limits
- Raise the provider's configured rateLimits if the account actually permits more throughput
- Register or enable additional providers so selection can route around a limited one
Example fix
// before const results = await Promise.all(tasks.map(t => adapter.execute(t))); // synchronized burst trips the limit // after import pLimit from 'p-limit'; const limit = pLimit(Math.floor(providerLimits.requestsPerMinute / 60)); // ~ steady rate const results = await Promise.all(tasks.map(t => limit(() => adapter.execute(t))));
Defensive patterns
Strategy: retry
Validate before calling
// Own the pacing: cap in-flight calls per provider under its configured limit const gate = new Semaphore(Math.floor(providerRateLimits.maxRequestsPerMinute / 60)); const run = () => gate.with(() => adapter.execute(task));
Try / catch
try {
await adapter.execute(task);
} catch (e) {
if (e instanceof Error && /rate limited/.test(e.message)) {
await sleep(msUntilNextMinuteWindow()); // window is 60s per rateLimits.resetAt
return adapter.execute(task); // exactly one rebounce, not a tight loop
}
throw e;
} Prevention
- Drive dispatch through a per-provider rate limiter sized to the configured limits
- Add jitter to fan-out so workers don't burst in lockstep
- Watch for 'rate-limited' status transitions and pre-emptively reroute to alternate providers
When it happens
Trigger: Executing or routing a task to a provider whose rateLimits counters tripped the 'rate-limited' status earlier in the same window (now < rateLimits.resetAt); the status only flips back to 'available' once the window resets (resetAt = last reset + 60000ms).
Common situations: Bursty fan-out (Promise.all over many tasks or workers) exceeding the provider's configured request/token limits; limits configured lower than the account allows; a shared provider saturated by another component of the system.
Related errors
- Amendment rate limit exceeded: ${this.maxAmendmentsPerWindow
- RATE_LIMIT
- RATE_LIMIT
- agent_execute failed
- Pinata upload failed: ${response.status} ${error}
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/aaac2a56f3566735.
Report an issue: GitHub.