ruvnet/ruflo · error

Hourly cost limit exceeded: $${this.hourlyCost.amount.toFixe

Error message

Hourly cost limit exceeded: $${this.hourlyCost.amount.toFixed(2)} / $${this.config.costLimitPerHour}

What it means

ProviderAdapter tracks accumulated spend in a rolling hour (hourlyCost); once the amount reaches config.costLimitPerHour, checkCostLimits refuses every further execution until the window resets (resetAt = last reset + 3600000ms). It is a hard cost circuit-breaker, not a warning.

Source

Thrown at v3/@claude-flow/integration/src/provider-adapter.ts:943

    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}`
      );
    }
  }

  /**
   * Get cached result
   */
  private getCachedResult(
    task: Task,
    providerId: string
  ): ExecutionResult | null {
    const cacheKey = `${providerId}:${task.id}:${task.description}`;
    const cached = this.cache.get(cacheKey);

    if (cached && Date.now() - cached.timestamp < this.config.cacheTTL!) {
      this.emit('cache-hit', { taskId: task.id, providerId });
      return cached.result;

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. If the budget genuinely allows, raise config.costLimitPerHour
  2. Otherwise wait for the hourly window to reset (up to 1h from the last reset)
  3. Route to cheaper providers/models or reduce token usage per task so hourly burn stays under the cap
  4. Track spend upstream and throttle submissions before the adapter's breaker trips

Example fix

// before
const adapter = new ProviderAdapterManager({ costLimitPerHour: 1 }); // trips minutes into a bulk run

// after
const adapter = new ProviderAdapterManager({ costLimitPerHour: 25 }); // sized to measured hourly burn
Defensive patterns

Strategy: validation

Validate before calling

// Mirror the adapter's rolling-hour accounting so you stop before it does
const spend = { amount: 0, resetAt: Date.now() + 3600_000 };
function canAfford(estimatedCost: number, cap: number): boolean {
  if (Date.now() >= spend.resetAt) {
    spend.amount = 0;
    spend.resetAt = Date.now() + 3600_000;
  }
  return spend.amount + estimatedCost < cap * 0.9; // 10% headroom
}

Try / catch

try {
  await adapter.execute(task);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Hourly cost limit exceeded')) {
    // pause the queue until the hour resets; do not create new adapter instances to dodge the cap
  }
  throw e;
}

Prevention

When it happens

Trigger: Any execution path that runs checkCostLimits after this hour's tracked spend has reached costLimitPerHour — the very next call throws, regardless of how cheap the target provider is.

Common situations: costLimitPerHour set below the workload's real burn rate (e.g. a $1 cap with token-heavy models), an expensive model selected for bulk jobs, or many workers sharing a single adapter instance and therefore one hourly budget.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/68db5a876df1b587. Report an issue: GitHub.