ruvnet/ruflo · error · TypeError
Policy canonicalization rejects non-finite numbers
Error message
Policy canonicalization rejects non-finite numbers
What it means
canonicalizePolicy() recursively normalizes a policy object (dropping undefined entries, sorting keys) to produce a deterministic JSON string used for policy hashing. While walking values it throws TypeError on any NaN/Infinity/-Infinity, because JSON.stringify would silently coerce those to null and two different policies could then hash identically.
Source
Thrown at v3/@claude-flow/security/src/policy/canonical.ts:14
import { createHash, createHmac, timingSafeEqual } from 'node:crypto';
function normalize(value: unknown): unknown {
if (Array.isArray(value)) return value.map(normalize);
if (value && typeof value === 'object') {
return Object.fromEntries(
Object.entries(value as Record<string, unknown>)
.filter(([, item]) => item !== undefined)
.sort(([a], [b]) => a.localeCompare(b))
.map(([key, item]) => [key, normalize(item)]),
);
}
if (typeof value === 'number' && !Number.isFinite(value)) {
throw new TypeError('Policy canonicalization rejects non-finite numbers');
}
return value;
}
export function canonicalizePolicy(value: unknown): string {
return JSON.stringify(normalize(value));
}
export function policyHash(value: unknown): string {
return `sha256:${createHash('sha256').update(canonicalizePolicy(value)).digest('hex')}`;
}
export function signPolicyHash(hash: string, key: string | Buffer): string {
return createHmac('sha256', key).update(hash).digest('base64url');
}
export function verifyPolicySignature(hash: string, signature: string, key: string | Buffer): boolean {
const expected = Buffer.from(signPolicyHash(hash, key));View on GitHub (pinned to fa13ee4ad6)
Solutions
- Find the offending key: log JSON.stringify of the policy with a replacer that flags non-finite numbers before hashing.
- Fix the upstream computation so it yields a finite number, or omit the key entirely (undefined entries are filtered out during normalization).
- Sanitize numeric config at load time: coerce NaN/Infinity to undefined and validate ranges.
Example fix
// before
const maxCost = Number(process.env.MAX_COST_USD); // NaN when unset
const hash = policyHash({ rules: [{ constraints: { maxCostUsd: maxCost } }] });
// after
const raw = Number(process.env.MAX_COST_USD);
const maxCost = Number.isFinite(raw) ? raw : undefined;
const hash = policyHash({ rules: [{ constraints: { maxCostUsd: maxCost } }] }); Defensive patterns
Strategy: validation
Validate before calling
function assertFiniteTree(value: unknown): void {
if (typeof value === 'number' && !Number.isFinite(value)) {
throw new TypeError(`non-finite number at path: ${pathOf(value)}`);
}
if (Array.isArray(value)) return value.forEach(assertFiniteTree);
if (value && typeof value === 'object') {
for (const v of Object.values(value)) assertFiniteTree(v);
}
}
assertFiniteTree(policy); // before policyHash(policy) Type guard
function isFiniteNumber(v: unknown): v is number {
return typeof v === 'number' && Number.isFinite(v);
} Try / catch
try {
return policyHash(policy);
} catch (err) {
if (err instanceof TypeError && /non-finite/.test(err.message)) {
logger.error('policy contains NaN/Infinity — check numeric config sources');
}
throw err;
} Prevention
- Wrap every Number(envVar) parse with Number.isFinite before storing it in policy state.
- Use undefined (omit the key) for 'no limit' — canonicalization drops undefined entries safely.
- Add a config-load assertion that walks the policy tree for finite numbers once at startup.
When it happens
Trigger: policyHash({ limit: NaN }) after Number('abc') or parseFloat of a non-numeric env value; { cost: Infinity } from dividing by zero; any nested rule object containing a computed number that degenerated to NaN.
Common situations: Numeric limits parsed from environment variables or CLI flags that are unset or misspelled; arithmetic on undefined values (undefined * 2 === NaN); JSON revivers that produce non-finite results.
Related errors
- invalid-policy-rule-limit
- invalid-budget-limit
- invalid-policy-action-${name}
- JCS canonicalization rejects non-finite numbers
- field "${key}" must be a finite number
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/3a1f4605c57ad5f0.
Report an issue: GitHub.