{"record":{"id":"5f91553a3ff7840c","repo":"ruvnet/ruflo","slug":"federation-canonicalization-rejects-cyclic-values","errorCode":null,"errorMessage":"Federation canonicalization rejects cyclic values","messagePattern":"Federation canonicalization rejects cyclic values","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugin-agent-federation/src/application/inbound-dispatcher.ts","lineNumber":171,"sourceCode":"      }\n      if (Number.isInteger(value) && !Number.isSafeInteger(value)) {\n        throw new TypeError('Federation canonicalization rejects unsafe integers');\n      }\n      return JSON.stringify(value);\n    case 'bigint':\n    case 'function':\n    case 'symbol':\n    case 'undefined':\n      throw new TypeError(`Federation canonicalization rejects ${typeof value}`);\n    case 'object':\n      break;\n    default:\n      throw new TypeError(`Federation canonicalization rejects ${typeof value}`);\n  }\n\n  const object = value as object;\n  if (ancestors.has(object)) {\n    throw new TypeError('Federation canonicalization rejects cyclic values');\n  }\n  ancestors.add(object);\n\n  try {\n    if (Array.isArray(value)) {\n      const items: string[] = [];\n      for (let index = 0; index < value.length; index += 1) {\n        if (!Object.prototype.hasOwnProperty.call(value, index)) {\n          throw new TypeError('Federation canonicalization rejects sparse arrays');\n        }\n        items.push(canonicalizeJcsValue(value[index], ancestors));\n      }\n      return `[${items.join(',')}]`;\n    }\n\n    const prototype = Object.getPrototypeOf(value);\n    if (prototype !== Object.prototype && prototype !== null) {\n      throw new TypeError('Federation canonicalization accepts only plain objects');","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugin-agent-federation/src/application/inbound-dispatcher.ts#L153-L189","documentation":"Cycle detection during JCS canonicalization: an ancestors set of in-progress objects catches circular references before recursion, throwing instead of overflowing the stack. JSON has no reference syntax, so a cyclic payload cannot be represented canonically and must be rejected — both sides could never agree on serialized bytes.","triggerScenarios":"Message metadata with parent-child cycles (a.child = b; b.parent = a); self-referential objects (obj.self = obj); shared back-references used to express DAGs inside signed payloads.","commonSituations":"Tree structures wired with parent pointers for convenience; ORM/graph model objects placed directly into envelopes; caches attaching bookkeeping back-links to cached nodes.","solutions":["Restructure the payload as a tree: replace back-references with an id the receiver resolves","Serialize explicitly with a mapper that emits plain data (no parent pointers)","Run a JSON.stringify pre-flight — it throws on cycles before you reach the canonicalizer, with a clearer origin"],"exampleFix":"// before\nmsg.meta.parent.child = msg.meta; // cycle\n// after\nmsg.meta.parent.childId = msg.meta.id; // reference by id","handlingStrategy":"validation","validationCode":"// cheap pre-flight: JSON.stringify throws on cycles before canonicalization\ntry {\n  JSON.stringify(envelope.metadata);\n} catch (e) {\n  throw new Error('refusing to sign cyclic metadata', { cause: e });\n}","typeGuard":"function isAcyclic(value: unknown, ancestors: WeakSet<object> = new WeakSet()): boolean {\n  if (typeof value !== 'object' || value === null) return true;\n  if (ancestors.has(value)) return false;\n  ancestors.add(value);\n  try {\n    return Object.values(value).every(v => isAcyclic(v, ancestors));\n  } finally {\n    ancestors.delete(value);\n  }\n}","tryCatchPattern":null,"preventionTips":["Model relationships by id reference, not object back-pointers","Run JSON.stringify as a pre-sign sanity check","Build envelopes via explicit mappers that emit plain trees","Keep parent-pointer convenience structures out of payloads"],"tags":["canonicalization","jcs","cycles","serialization"],"backgroundTag":"circular-reference","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}