{"record":{"id":"7106ba092049bfae","repo":"ruvnet/ruflo","slug":"undefined-property-at-path-key","errorCode":null,"errorMessage":"undefined property at ${path}.${key}","messagePattern":"undefined property at (.+?)\\.(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/services/flywheel-receipt.ts","lineNumber":164,"sourceCode":"\nfunction assertJsonValue(value: unknown, path = '$'): void {\n  if (value === null || typeof value === 'string' || typeof value === 'boolean') return;\n  if (typeof value === 'number') {\n    if (!Number.isFinite(value) || Object.is(value, -0)) {\n      throw new Error(`non-canonical number at ${path}`);\n    }\n    return;\n  }\n  if (Array.isArray(value)) {\n    value.forEach((v, i) => {\n      if (v === undefined) throw new Error(`undefined array member at ${path}[${i}]`);\n      assertJsonValue(v, `${path}[${i}]`);\n    });\n    return;\n  }\n  if (typeof value === 'object') {\n    for (const [key, child] of Object.entries(value as Record<string, unknown>)) {\n      if (child === undefined) throw new Error(`undefined property at ${path}.${key}`);\n      assertJsonValue(child, `${path}.${key}`);\n    }\n    return;\n  }\n  throw new Error(`unsupported JSON value at ${path}`);\n}\n\n/**\n * RFC-8785-compatible for the JSON domain accepted above: ECMAScript primitive\n * serialization plus recursively sorted UTF-16 property names.\n */\nexport function canonicalizeJcs(value: unknown): string {\n  assertJsonValue(value);\n  const encode = (v: unknown): string => {\n    if (v === null || typeof v !== 'object') return JSON.stringify(v);\n    if (Array.isArray(v)) return `[${v.map(encode).join(',')}]`;\n    const obj = v as Record<string, unknown>;\n    return `{${Object.keys(obj).sort().map((k) => `${JSON.stringify(k)}:${encode(obj[k])}`).join(',')}}`;","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/services/flywheel-receipt.ts#L146-L182","documentation":"Thrown by assertJsonValue() when an object property's value is `undefined`. Object.entries() yields keys whose value is undefined (unlike JSON.stringify which silently drops them), so canonicalization must reject them explicitly to keep the hash deterministic across the JSON domain. The ${path}.${key} placeholder names the offending property.","triggerScenarios":"Passing an object to createFlywheelReceipt()/canonicalizeJcs() where a property was set to undefined (e.g. { a: 1, b: undefined }) rather than deleted. Common with spread/merge of partial configs, optional fields defaulted to undefined, or JSON parsed then re-typed.","commonSituations":"Candidate policy assembled via { ...defaults, tuningParam: opts.tuningParam } where opts.tuningParam is undefined; resourceEvidence or gate maps with conditionally-undefined entries.","solutions":["Strip undefined properties before canonicalization: Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined)).","Delete the key instead of assigning undefined at construction time.","Use null where a JSON-representable absent value is needed.","Validate the policy object with a helper that rejects undefined values recursively."],"exampleFix":"// before\nconst policy = { ...base, alpha: opts.alpha };\n// opts.alpha is undefined → triggers on canonicalizeJcs\n// after\nconst policy = { ...base };\nif (opts.alpha !== undefined) policy.alpha = opts.alpha;","handlingStrategy":"validation","validationCode":"function stripUndefinedProps<T extends Record<string, unknown>>(obj: T): Partial<T> {\n  return Object.fromEntries(\n    Object.entries(obj).filter(([, v]) => v !== undefined)\n  ) as Partial<T>;\n}\nconst cleanPolicy = stripUndefinedProps(candidatePolicy);","typeGuard":"function hasNoUndefinedProps(value: unknown): boolean {\n  if (value && typeof value === 'object') {\n    return Object.entries(value).every(([k, v]) => v !== undefined && hasNoUndefinedProps(v));\n  }\n  return value !== undefined;\n}","tryCatchPattern":"try {\n  canonicalizeJcs(payload);\n} catch (e) {\n  if (e instanceof Error && /undefined property/.test(e.message)) {\n    // strip and retry, or surface a precise config error\n    payload = JSON.parse(JSON.stringify(payload));\n  } else throw e;\n}","preventionTips":["Prefer deleting keys over assigning undefined when a field is absent.","When merging partial configs, filter undefined entries explicitly.","Use a schema validator (zod) that rejects undefined on optional fields."],"tags":["validation","serialization","json","canonicalization","receipt"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}