ruvnet/ruflo · error · TypeError

Federation canonicalization rejects ${typeof value}

Error message

Federation canonicalization rejects ${typeof value}

What it means

The JCS canonicalizer only handles JSON-representable values: bigint, function, symbol, and undefined all hit this rejection because they have no canonical JSON form and cannot participate in a deterministically signed payload. This is the explicit case list in the switch, distinct from the defensive default branch below it.

Source

Thrown at v3/@claude-flow/plugin-agent-federation/src/application/inbound-dispatcher.ts:162

  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)) {
    throw new TypeError('Federation canonicalization rejects cyclic values');
  }
  ancestors.add(object);

  try {
    if (Array.isArray(value)) {
      const items: string[] = [];
      for (let index = 0; index < value.length; index += 1) {
        if (!Object.prototype.hasOwnProperty.call(value, index)) {
          throw new TypeError('Federation canonicalization rejects sparse arrays');

View on GitHub (pinned to fa13ee4ad6)

Solutions

  1. Omit undefined-valued keys instead of including them (build payloads via JSON round-trip or explicit deletion)
  2. Convert BigInt with String(value) before placing it in the envelope
  3. Ensure signed payloads originate from JSON.parse of received data or plain literals only
  4. Add a deep scan before signing that rejects bigint/function/symbol/undefined anywhere in the tree

Example fix

// before
metadata.traceId = processBigintId(); // bigint
// after
metadata.traceId = String(processBigintId());
Defensive patterns

Strategy: type-guard

Validate before calling

// strip undefined keys and convert bigints before signing
function toSignable<T>(value: T): T {
  return JSON.parse(
    JSON.stringify(value, (_k, v) => (typeof v === 'bigint' ? String(v) : v)),
  );
}

Type guard

function isCanonicalValueKind(v: unknown): boolean {
  const t = typeof v;
  return (
    t === 'string' ||
    t === 'number' ||
    t === 'boolean' ||
    (t === 'object' && v !== null)
  );
}

Prevention

When it happens

Trigger: BigInt IDs or counters stored in metadata (values from BigInt arithmetic); optional fields left as undefined instead of being omitted; property copies off class instances dragging function values along; Symbols used as enum-like keys with runtime values.

Common situations: Migrating numeric IDs to BigInt for range reasons; partial-object updates where absent fields stay undefined; objects built from class instances via spread.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/061ba6a5afc2eb80. Report an issue: GitHub.