ruvnet/ruflo · error · Error
invalid-budget-limit
Error message
invalid-budget-limit
What it means
The second gate in setBudget(): periodMs must itself be a finite number, and optional caps maxCostUsd / maxTokens must be finite and non-negative when present. Error('invalid-budget-limit'). This is distinct from invalid-budget-period, which fires first for periodMs <= 0 — this one catches Infinity periodMs and bad cap values.
Source
Thrown at v3/@claude-flow/security/src/policy/engine.ts:99
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
|| record.expiresAt <= issuedAt
|| !Number.isInteger(record.maxUses)
|| record.maxUses <= 0
|| !Number.isInteger(record.uses)View on GitHub (pinned to fa13ee4ad6)
Solutions
- Omit maxCostUsd/maxTokens (leave undefined) when there is no cap for that dimension.
- Sanitize parsed numbers at config load: keep only finite, non-negative values, else undefined.
- Ensure periodMs is a finite positive integer of milliseconds.
Example fix
// before
engine.setBudget({ id: 'b', periodMs: 3600_000, maxCostUsd: Number('') }); // NaN throws
// after
const cap = Number(env.MAX_COST);
engine.setBudget({
id: 'b',
periodMs: 3600_000,
maxCostUsd: Number.isFinite(cap) && cap >= 0 ? cap : undefined,
}); Defensive patterns
Strategy: validation
Validate before calling
const cap = (v: number | undefined) =>
v === undefined ? undefined : Number.isFinite(v) && v >= 0 ? v : undefined;
engine.setBudget({
id,
periodMs: 3600_000,
maxCostUsd: cap(rawCost),
maxTokens: cap(rawTokens),
}); Type guard
function isSanitizedBudget(b: BudgetLimit): boolean {
const caps = [b.maxCostUsd, b.maxTokens].filter((v) => v !== undefined);
return Number.isFinite(b.periodMs) && caps.every((v) => Number.isFinite(v as number) && (v as number) >= 0);
} Try / catch
try {
engine.setBudget(limit);
} catch (err) {
if (err instanceof Error && err.message === 'invalid-budget-limit') {
throw new ConfigError(`budget ${limit.id}: caps must be finite and >= 0`);
}
throw err;
} Prevention
- Omit cap fields rather than passing NaN/Infinity/negatives — undefined means 'no cap' here.
- Share one numeric-sanitizer between setBudget, upsertRule, and evaluate inputs.
- Log raw parsed env values at startup so bad caps are visible before they reach the engine.
When it happens
Trigger: periodMs: Infinity (e.g. from a bad 'never resets' computation); maxCostUsd: NaN parsed from an unset env var; maxTokens: -100 from inverted arithmetic; maxCostUsd: Infinity intended as 'unlimited'.
Common situations: Env-parsed caps with unset variables; expressing 'unlimited' with Infinity or negative sentinels; copy-pasting limits between systems with different conventions.
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
- Policy canonicalization rejects non-finite numbers
- invalid-policy-rule-limit
- invalid-budget-period
- invalid-policy-action-${name}
- estimatedUsd must be a non-negative finite number
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/3d600df1f6fd3560.
Report an issue: GitHub.