ruvnet/ruflo · error · Error
unsupported JSON value at ${path}
Error message
unsupported JSON value at ${path} What it means
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.
Source
Thrown at v3/@claude-flow/cli/src/services/flywheel-receipt.ts:169
throw new Error(`non-canonical number at ${path}`);
}
return;
}
if (Array.isArray(value)) {
value.forEach((v, i) => {
if (v === undefined) throw new Error(`undefined array member at ${path}[${i}]`);
assertJsonValue(v, `${path}[${i}]`);
});
return;
}
if (typeof value === 'object') {
for (const [key, child] of Object.entries(value as Record<string, unknown>)) {
if (child === undefined) throw new Error(`undefined property at ${path}.${key}`);
assertJsonValue(child, `${path}.${key}`);
}
return;
}
throw new Error(`unsupported JSON value at ${path}`);
}
/**
* RFC-8785-compatible for the JSON domain accepted above: ECMAScript primitive
* serialization plus recursively sorted UTF-16 property names.
*/
export function canonicalizeJcs(value: unknown): string {
assertJsonValue(value);
const encode = (v: unknown): string => {
if (v === null || typeof v !== 'object') return JSON.stringify(v);
if (Array.isArray(v)) return `[${v.map(encode).join(',')}]`;
const obj = v as Record<string, unknown>;
return `{${Object.keys(obj).sort().map((k) => `${JSON.stringify(k)}:${encode(obj[k])}`).join(',')}}`;
};
return encode(value);
}
export function sha256Ref(value: string | Buffer): string {View on GitHub (pinned to 6b01dc5a68)
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().
Example fix
// before
const policy = { ts: 1700000000n, weight: 0.5 };
policyCandidateId(policy);
// after
const policy = { ts: '1700000000', weight: 0.5 };
policyCandidateId(policy); Defensive patterns
Strategy: type-guard
Validate before calling
function assertJsonSafe(value: unknown, path = '$'): void {
if (value === null || typeof value === 'string' || typeof value === 'boolean') return;
if (typeof value === 'number') return;
if (Array.isArray(value)) { value.forEach((v, i) => assertJsonSafe(v, `${path}[${i}]`)); return; }
if (value && typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype || value && typeof value === 'object' && Object.getPrototypeOf(value) === null) {
for (const [k, v] of Object.entries(value)) assertJsonSafe(v, `${path}.${k}`);
return;
}
throw new TypeError(`non-JSON value at ${path}: ${typeof value}`);
}
assertJsonSafe(candidatePolicy); Type guard
function isPlainJsonValue(value: unknown): value is null | string | boolean | number | unknown[] | Record<string, unknown> {
if (value === null || ['string', 'boolean', 'number'].includes(typeof value)) return true;
if (Array.isArray(value)) return value.every(isPlainJsonValue);
if (value && typeof value === 'object') {
const proto = Object.getPrototypeOf(value);
if (proto !== Object.prototype && proto !== null) return false;
return Object.values(value).every(isPlainJsonValue);
}
return false;
} Try / catch
try {
policyCandidateId(policy);
} catch (e) {
if (e instanceof Error && /unsupported JSON value/.test(e.message)) {
policy = JSON.parse(JSON.stringify(policy, (_k, v) => typeof v === 'bigint' ? v.toString() : v));
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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().
Common situations: 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.
Related errors
- undefined array member at ${path}[${i}]
- undefined property at ${path}.${key}
- ${label} requires a JSON argument
- ${label} must be valid JSON
- ${toolsJson} must contain a JSON array of {name, description
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/92a81b1722720415.
Report an issue: GitHub.