ruvnet/ruflo · error · TypeError
JCS canonicalization rejects non-finite numbers
Error message
JCS canonicalization rejects non-finite numbers
What it means
canonicalizeProductPlane() implements RFC 8785 (JCS) canonical JSON for the product plane; JCS has no representation for NaN, Infinity, or -Infinity (JSON.stringify would emit null and break cross-implementation canonical form), so any non-finite number in the payload throws TypeError immediately.
Source
Thrown at v3/@claude-flow/security/src/policy/product-plane.ts:1098
}
}
/**
* RFC 8785/JCS-compatible canonical JSON for this I-JSON profile.
*
* ECMAScript's JSON number serialization supplies the JCS number rendering.
* Non-finite numbers, sparse arrays, undefined values, non-plain objects, and
* invalid Unicode are rejected instead of being silently coerced.
*/
export function canonicalizeProductPlane(value: unknown): string {
if (value === null) return 'null';
if (typeof value === 'boolean') return value ? 'true' : 'false';
if (typeof value === 'string') {
assertUnicodeScalarString(value);
return JSON.stringify(value);
}
if (typeof value === 'number') {
if (!Number.isFinite(value)) throw new TypeError('JCS canonicalization rejects non-finite numbers');
return JSON.stringify(value);
}
if (Array.isArray(value)) {
const items: string[] = [];
for (let index = 0; index < value.length; index++) {
if (!Object.prototype.hasOwnProperty.call(value, index)) {
throw new TypeError('JCS canonicalization rejects sparse arrays');
}
items.push(canonicalizeProductPlane(value[index]));
}
return `[${items.join(',')}]`;
}
if (!isRecord(value)) {
throw new TypeError('JCS canonicalization accepts only JSON-compatible plain objects');
}
const entries: string[] = [];
for (const key of Object.keys(value).sort()) {
assertUnicodeScalarString(key);View on GitHub (pinned to fa13ee4ad6)
Solutions
- Sanitize the tree before canonicalization: walk it and throw or replace non-finite numbers with null/a finite default at the source.
- Fix the computation producing NaN/Infinity (guard divisions, default missing inputs to 0 or omit the field).
- If the field is optional, drop the key so canonicalization never sees the value.
Example fix
// before
const payload = { usage: { ratio: tokens / seconds } }; // seconds=0 -> Infinity
canonicalizeProductPlane(payload); // throws
// after
const ratio = seconds > 0 ? tokens / seconds : null;
canonicalizeProductPlane({ usage: { ratio } }); Defensive patterns
Strategy: validation
Validate before calling
function assertFiniteNumbers(value: unknown, at = '$'): void {
if (typeof value === 'number' && !Number.isFinite(value)) {
throw new TypeError(`non-finite number at ${at}: ${value}`);
}
if (Array.isArray(value)) return value.forEach((v, i) => assertFiniteNumbers(v, `${at}[${i}]`));
if (value && typeof value === 'object') {
for (const [k, v] of Object.entries(value)) assertFiniteNumbers(v, `${at}.${k}`);
}
}
assertFiniteNumbers(payload);
const canonical = canonicalizeProductPlane(payload); Type guard
function isFiniteNumber(v: unknown): v is number {
return typeof v === 'number' && Number.isFinite(v);
} Try / catch
try {
return canonicalizeProductPlane(payload);
} catch (err) {
if (err instanceof TypeError && /non-finite/.test(err.message)) {
throw new PayloadError('payload contains NaN/Infinity — fix the metric producer before signing');
}
throw err;
} Prevention
- Guard all divisions (denominator > 0) and default missing metrics to 0 or omit the field.
- Walk payloads with a finite-number assertion before hashing/signing so failures name the field path.
- Never accept non-finite numbers from JSON.parse revivers or external producers into hashable payloads.
When it happens
Trigger: canonicalizeProductPlane({ score: NaN }) after aggregating undefined metrics; { ratio: x / 0 } yielding Infinity; payloads built from JSON.parse of documents containing 'Infinity' literals or from float math on missing data.
Common situations: Analytics/telemetry aggregation where a divide-by-zero or missing input degenerates to Infinity/NaN; passing JS computation results straight into a signed/hashable payload; legacy producers that emit non-finite numbers and relied on tolerant JSON elsewhere.
Related errors
- Policy canonicalization rejects non-finite numbers
- JCS canonicalization rejects an unpaired high surrogate
- JCS canonicalization rejects an unpaired low surrogate
- field "${key}" must be a finite number
- Federation canonicalization rejects non-canonical numbers
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/71e094abeb0cb091.
Report an issue: GitHub.