{"record":{"id":"5c16fcb6943c643c","repo":"ruvnet/ruflo","slug":"federation-canonicalization-rejects-sparse-arrays","errorCode":null,"errorMessage":"Federation canonicalization rejects sparse arrays","messagePattern":"Federation canonicalization rejects sparse arrays","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/plugin-agent-federation/src/application/inbound-dispatcher.ts","lineNumber":180,"sourceCode":"      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');\n    }\n\n    const record = value as Record<string, unknown>;\n    const entries = Object.keys(record)\n      .sort()\n      .map((key) => `${JSON.stringify(key)}:${canonicalizeJcsValue(record[key], ancestors)}`);\n    return `{${entries.join(',')}}`;\n  } finally {\n    ancestors.delete(object);","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/plugin-agent-federation/src/application/inbound-dispatcher.ts#L162-L198","documentation":"JCS canonicalization rejects arrays with holes: every index from 0 to length-1 is checked with hasOwnProperty, so [1,,3], new Array(5) with partial assignment, or arrays after delete all fail. Sparse arrays have no representation in JSON's array grammar, and hole handling differs across implementations, which would break byte-exact signature agreement.","triggerScenarios":"Building arrays with new Array(n) then assigning only some slots; using delete arr[i]; arrays produced by constructs that skip indices; results collected with sparse index assignment.","commonSituations":"Slot reservation patterns (const slots = new Array(count)); removing elements with delete instead of splice; data assembled from indexed writes at non-contiguous positions.","solutions":["Preallocate with Array.from({ length: n }, () => null) or .fill(null) so every slot exists","Replace delete arr[i] with splice(i, 1) or assignment of null","Densify before signing: map holes to null (e.g. arr.map((v, i) => i in arr ? v : null))","Add a pre-sign assertion that every index below length is an own property"],"exampleFix":"// before\nconst slots = new Array(count);\nslots[0] = a; slots[2] = c; // hole at index 1\n// after\nconst slots = Array.from({ length: count }, () => null);\nslots[0] = a; slots[2] = c;","handlingStrategy":"type-guard","validationCode":"// densify before signing: give every hole an explicit null\nconst dense = Array.from(arr, (v, i) => (i in arr ? v : null));","typeGuard":"function isDenseArray(a: unknown[]): boolean {\n  return a.every((_, i) => Object.prototype.hasOwnProperty.call(a, i));\n}","tryCatchPattern":null,"preventionTips":["Preallocate with Array.from({ length: n }, () => null), not new Array(n)","Use splice instead of delete on array elements","Assert density on arrays built from indexed writes before signing","Prefer push/concat builders that never skip indices"],"tags":["canonicalization","jcs","arrays"],"backgroundTag":"sparse-array","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"}