ruvnet/ruflo · error · Error
invalid-policy-rule-limit
Error message
invalid-policy-rule-limit
What it means
When a rule defines optional numeric constraints (maxCostUsd, maxTokens, maxConcurrency), upsertRule() checks each: a value that is present but non-finite or negative throws Error('invalid-policy-rule-limit'). Limits are caps, so NaN, Infinity, and negative numbers are all meaningless and rejected.
Source
Thrown at v3/@claude-flow/security/src/policy/engine.ts:86
setMode(mode: PolicyState['mode']): void {
this.state.mode = mode;
}
setConfiguredMode(mode: PolicyState['mode']): void {
this.state.configuredMode = mode;
const rank = { legacy: 0, observe: 1, enforce: 2 } as const;
if (rank[mode] > rank[this.state.mode]) this.state.mode = mode;
}
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));
}
View on GitHub (pinned to fa13ee4ad6)
Solutions
- Use undefined (omit the key) to express 'no limit' — the check only applies when the value is defined.
- Guard numeric env parsing: only assign when Number.isFinite(v) && v >= 0.
- Correct negative values to zero or a positive cap depending on intent.
Example fix
// before
const maxTokens = Number(env.MAX_TOKENS); // NaN when unset -> throws
engine.upsertRule({ id: 'r', actions: ['run'], constraints: { maxTokens } });
// after
const parsed = Number(env.MAX_TOKENS);
const maxTokens = Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;
engine.upsertRule({ id: 'r', actions: ['run'], constraints: { maxTokens } }); Defensive patterns
Strategy: validation
Validate before calling
function sanitizeLimit(v: number | undefined): number | undefined {
if (v === undefined) return undefined;
if (!Number.isFinite(v) || v < 0) throw new Error(`invalid limit: ${v}`);
return v;
}
engine.upsertRule({
id: 'r',
actions: ['run'],
constraints: {
maxCostUsd: sanitizeLimit(parsedCost),
maxTokens: sanitizeLimit(parsedTokens),
maxConcurrency: sanitizeLimit(parsedConcurrency),
},
}); Type guard
function isValidLimit(v: unknown): v is number {
return v === undefined || (typeof v === 'number' && Number.isFinite(v) && v >= 0);
} Try / catch
try {
engine.upsertRule(rule);
} catch (err) {
if (err instanceof Error && err.message === 'invalid-policy-rule-limit') {
throw new ConfigError(`rule ${rule.id} has a non-finite or negative constraint`);
}
throw err;
} Prevention
- Express 'unlimited' by omitting the constraint key, never with -1 or Infinity.
- Centralize env->number parsing in one helper that returns undefined for non-finite results.
- Unit-test config parsing with unset, empty, and negative values.
When it happens
Trigger: constraints: { maxCostUsd: -1 } from a sign typo; { maxTokens: NaN } from parsing 'unlimited' or an empty env var; { maxConcurrency: Infinity } used to mean 'no cap'.
Common situations: Env-driven config where an unset variable parses to NaN; using -1 as a 'disabled' sentinel (common in other systems) which this engine rejects; spreadsheet-edited policy files with negative 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
- Policy canonicalization rejects non-finite numbers
- invalid-budget-limit
- invalid-policy-action-${name}
- field "${key}" must be a finite number
- mode must be legacy, observe, or enforce
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/e588b4c45b3d4116.
Report an issue: GitHub.