{"record":{"id":"92a81b1722720415","repo":"ruvnet/ruflo","slug":"unsupported-json-value-at-path","errorCode":null,"errorMessage":"unsupported JSON value at ${path}","messagePattern":"unsupported JSON value at (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/services/flywheel-receipt.ts","lineNumber":169,"sourceCode":"      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(',')}}`;\n  };\n  return encode(value);\n}\n\nexport function sha256Ref(value: string | Buffer): string {","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/services/flywheel-receipt.ts#L151-L187","documentation":"Thrown by assertJsonValue() when a value is not null, string, boolean, number, array, or plain object — i.e. it falls outside the canonical JSON domain. This catches Symbol, Function, BigInt, class instances, and any other non-JSON-serializable type that would break deterministic hashing.","triggerScenarios":"Passing a value containing a BigInt (e.g. a timestamp as BigInt), a Symbol-keyed or Symbol-valued entry, a Function reference, a class instance (not a plain object literal), or a Map/Set to canonicalizeJcs(), createFlywheelReceipt(), or policyCandidateId().","commonSituations":"Candidate policy that includes a BigInt metric (common in crypto/timestamp code); a policy object that is a class instance rather than a plain record; values produced by libraries that return custom types.","solutions":["Convert BigInt fields to string or number before passing (e.g. ts.toString()).","Serialize class instances to plain objects (JSON.parse(JSON.stringify(obj)) or a manual mapper).","Strip Symbol/Function values from the object tree before canonicalization.","Ensure only plain Record<string, unknown> / JSON-native values reach canonicalizeJcs()."],"exampleFix":"// before\nconst policy = { ts: 1700000000n, weight: 0.5 };\npolicyCandidateId(policy);\n// after\nconst policy = { ts: '1700000000', weight: 0.5 };\npolicyCandidateId(policy);","handlingStrategy":"type-guard","validationCode":"function assertJsonSafe(value: unknown, path = '$'): void {\n  if (value === null || typeof value === 'string' || typeof value === 'boolean') return;\n  if (typeof value === 'number') return;\n  if (Array.isArray(value)) { value.forEach((v, i) => assertJsonSafe(v, `${path}[${i}]`)); return; }\n  if (value && typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype || value && typeof value === 'object' && Object.getPrototypeOf(value) === null) {\n    for (const [k, v] of Object.entries(value)) assertJsonSafe(v, `${path}.${k}`);\n    return;\n  }\n  throw new TypeError(`non-JSON value at ${path}: ${typeof value}`);\n}\nassertJsonSafe(candidatePolicy);","typeGuard":"function isPlainJsonValue(value: unknown): value is null | string | boolean | number | unknown[] | Record<string, unknown> {\n  if (value === null || ['string', 'boolean', 'number'].includes(typeof value)) return true;\n  if (Array.isArray(value)) return value.every(isPlainJsonValue);\n  if (value && typeof value === 'object') {\n    const proto = Object.getPrototypeOf(value);\n    if (proto !== Object.prototype && proto !== null) return false;\n    return Object.values(value).every(isPlainJsonValue);\n  }\n  return false;\n}","tryCatchPattern":"try {\n  policyCandidateId(policy);\n} catch (e) {\n  if (e instanceof Error && /unsupported JSON value/.test(e.message)) {\n    policy = JSON.parse(JSON.stringify(policy, (_k, v) => typeof v === 'bigint' ? v.toString() : v));\n  } else throw e;\n}","preventionTips":["Never pass BigInt directly — coerce to string at the boundary.","Serialize class instances to plain objects before hashing.","Avoid Symbol/Function anywhere in policy records."],"tags":["validation","serialization","json","type-error","receipt"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}