ruvnet/ruflo · error · TypeError
Federation canonicalization rejects non-canonical numbers
Error message
Federation canonicalization rejects non-canonical numbers
What it means
During JCS (RFC 8785-style) canonicalization of a signed federation envelope, numbers must be exactly representable: NaN, ±Infinity, and negative zero (-0) are rejected because their JSON serialization is undefined or ambiguous, and sender/receiver would verify different bytes. The check uses Number.isFinite plus Object.is(value, -0).
Source
Thrown at v3/@claude-flow/plugin-agent-federation/src/application/inbound-dispatcher.ts:152
* values outside the I-JSON-safe subset instead of silently dropping them.
* The `signature` field itself is excluded because it is what we verify.
*
* Federation messages are wrapped as `AgentMessage{id, type, payload,
* metadata}` on the wire. The `payload` is the actual FederationEnvelope
* (per `plugin.ts sendToNode`); we canonicalize the payload + the
* metadata so the receiver verifies the same bytes the sender signed.
*/
function canonicalizeJcsValue(value: unknown, ancestors: Set<object>): string {
if (value === null) return 'null';
switch (typeof value) {
case 'boolean':
return value ? 'true' : 'false';
case 'string':
return JSON.stringify(value);
case 'number':
if (!Number.isFinite(value) || Object.is(value, -0)) {
throw new TypeError('Federation canonicalization rejects non-canonical numbers');
}
if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
throw new TypeError('Federation canonicalization rejects unsafe integers');
}
return JSON.stringify(value);
case 'bigint':
case 'function':
case 'symbol':
case 'undefined':
throw new TypeError(`Federation canonicalization rejects ${typeof value}`);
case 'object':
break;
default:
throw new TypeError(`Federation canonicalization rejects ${typeof value}`);
}
const object = value as object;
if (ancestors.has(object)) {View on GitHub (pinned to fa13ee4ad6)
Solutions
- Sanitize every numeric before it enters a signed envelope: Number.isFinite(v) && !Object.is(v, -0)
- Normalize -0 to 0 (v + 0 or `v === 0 ? 0 : v`)
- Convey NaN/Infinity as explicit strings or null if they must be transmitted
- Add a pre-sign assertion that walks the payload and rejects non-canonical numbers
Example fix
// before envelope.metadata.ratio = numerator / denominator; // 0/0 -> NaN // after const raw = numerator / denominator; envelope.metadata.ratio = Number.isFinite(raw) && !Object.is(raw, -0) ? raw : null;
Defensive patterns
Strategy: type-guard
Validate before calling
// sanitize numbers before they enter a signed envelope
function sanitizeNumber(v: number): number | null {
return Number.isFinite(v) && !Object.is(v, -0) ? v : null;
} Type guard
function isCanonicalNumber(v: unknown): v is number {
return (
typeof v === 'number' &&
Number.isFinite(v) &&
!Object.is(v, -0)
);
} Prevention
- Never put raw division/averaging results into signed metadata
- Normalize -0 to 0 with `v + 0` when zero is possible
- Convey NaN/Infinity as strings or null
- Walk the payload with a pre-sign assertion rejecting non-canonical numbers
When it happens
Trigger: Envelope metadata carrying NaN or Infinity from a failed computation (0/0, overflow); a -0 produced by Math.round(-0.4), a sign flip, or parsing '-0'; division or averaging code writing raw results into signed metadata.
Common situations: Metrics/latency fields (elapsed = end - start where both are 0) copied into message metadata; normalization math that can yield -0; user-supplied numbers passed through unvalidated.
Related errors
- Federation canonicalization rejects unsafe integers
- Federation canonicalization rejects ${typeof value}
- Federation canonicalization rejects cyclic values
- Federation canonicalization rejects sparse arrays
- Federation canonicalization accepts only plain objects
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/20d8f146822c4e9b.
Report an issue: GitHub.