ruvnet/ruflo · error · Error

invalid-budget-period

Error message

invalid-budget-period

What it means

PolicyEngine.setBudget() rejects a BudgetLimit whose periodMs is <= 0 with Error('invalid-budget-period'). A budget window of zero or negative duration can never accrue, so it is treated as a programming error rather than an empty budget.

Source

Thrown at v3/@claude-flow/security/src/policy/engine.ts:95

  upsertRule(rule: PolicyRule): void {
    if (!rule.id || !rule.actions.length) throw new Error('invalid-policy-rule');
    for (const value of [
      rule.constraints?.maxCostUsd,
      rule.constraints?.maxTokens,
      rule.constraints?.maxConcurrency,
    ]) {
      if (value !== undefined && (!Number.isFinite(value) || value < 0)) {
        throw new Error('invalid-policy-rule-limit');
      }
    }
    const index = this.state.rules.findIndex((item) => item.id === rule.id);
    if (index >= 0) this.state.rules[index] = structuredClone(rule);
    else this.state.rules.push(structuredClone(rule));
  }

  setBudget(limit: BudgetLimit): void {
    if (limit.periodMs <= 0) throw new Error('invalid-budget-period');
    if (!Number.isFinite(limit.periodMs)
      || [limit.maxCostUsd, limit.maxTokens].some((value) => (
        value !== undefined && (!Number.isFinite(value) || value < 0)
      ))) throw new Error('invalid-budget-limit');
    const index = this.state.budgets.findIndex((item) => item.id === limit.id);
    if (index >= 0) this.state.budgets[index] = structuredClone(limit);
    else this.state.budgets.push(structuredClone(limit));
  }

  issueApproval(approval: Omit<PolicyApproval, 'uses' | 'issuedAt'> & { uses?: number; issuedAt?: number }): PolicyApproval {
    if (approval.issuedBy === approval.principal) throw new Error('self-approval-forbidden');
    if (this.approvalIssuerVerifier?.(approval.issuedBy) !== true) {
      throw new Error('untrusted-approval-issuer');
    }
    const issuedAt = approval.issuedAt ?? this.now();
    const record: PolicyApproval = { ...approval, issuedAt, uses: approval.uses ?? 0 };
    if (this.state.approvals.some((item) => item.id === record.id)) throw new Error('duplicate-approval-id');
    if (!record.id

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Set a positive period in milliseconds, e.g. 3_600_000 for one hour.
  2. When deriving periodMs from timestamps, validate start < end before constructing the limit.
  3. Reject budget config at load time if periodMs is missing or non-positive.

Example fix

// before
engine.setBudget({ id: 'daily', periodMs: 0, maxCostUsd: 5 });

// after
engine.setBudget({ id: 'daily', periodMs: 24 * 3_600_000, maxCostUsd: 5 });
Defensive patterns

Strategy: validation

Validate before calling

if (!Number.isFinite(budget.periodMs) || budget.periodMs <= 0) {
  throw new Error(`budget ${budget.id}: periodMs must be > 0 (got ${budget.periodMs})`);
}
engine.setBudget(budget);

Type guard

function hasPositivePeriod(b: BudgetLimit): boolean {
  return Number.isFinite(b.periodMs) && b.periodMs > 0;
}

Try / catch

try {
  engine.setBudget(budget);
} catch (err) {
  if (err instanceof Error && err.message === 'invalid-budget-period') {
    return configError(`budget '${budget.id}' needs a positive periodMs`);
  }
  throw err;
}

Prevention

When it happens

Trigger: setBudget({ id: 'b', periodMs: 0 }) from a default placeholder; periodMs computed as end - start where the timestamps are equal or reversed (negative result); passing seconds (e.g. 60) where a multi-hour window was expected still works, but 0 or negatives throw.

Common situations: Computing the window from two Date.now() calls in quick succession; config files that omit the period key and default it to 0; unit tests constructing budgets with dummy values.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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