prisma/prisma · error · TypeError
canonicalStringify: objects with symbol-keyed properties are
Error message
canonicalStringify: objects with symbol-keyed properties are not supported
What it means
Thrown by canonicalStringify's writePlainObject when a plain object has one or more symbol-keyed own properties (detected via Object.getOwnPropertySymbols). Object.keys ignores symbol keys, so they would be silently dropped from the canonical form, producing a key that omits real data. The function refuses to produce a lossy key.
Source
Thrown at packages/1-framework/0-foundation/utils/src/canonical-stringify.ts:138
}
function writePlainObject(obj: Record<string, unknown>, seen: Set<object>): string {
// Only true plain objects are accepted here. Without this guard, anything
// that fell through the type-tagged branches above (`Map`, `Set`,
// `RegExp`, class instances, …) would canonicalize to `{}` because
// `Object.keys` returns no enumerable string keys for them — silently
// colliding with each other and with the literal `{}`.
const proto = Object.getPrototypeOf(obj);
if (proto !== Object.prototype && proto !== null) {
const tag = proto?.constructor?.name ?? 'unknown';
throw new TypeError(`canonicalStringify: non-plain objects are not supported (got ${tag})`);
}
// `Object.keys` ignores symbol-keyed properties, so they would be
// silently dropped from the canonical form. Force callers to handle
// them explicitly instead of producing a key that omits real data.
if (Object.getOwnPropertySymbols(obj).length > 0) {
throw new TypeError(
'canonicalStringify: objects with symbol-keyed properties are not supported',
);
}
const keys = Object.keys(obj).sort();
const parts: string[] = [];
for (const key of keys) {
parts.push(`${JSON.stringify(key)}:${write(obj[key], seen)}`);
}
return `{${parts.join(',')}}`;
}
function bytesToHex(bytes: Uint8Array): string {
let out = '';
for (let i = 0; i < bytes.length; i++) {
const byte = bytes[i] as number;
out += byte.toString(16).padStart(2, '0');
}View on GitHub (pinned to a20d61fb6f)
Solutions
- Project the object to a new plain object containing only the string-keyed properties that form its identity.
- Explicitly convert symbol-keyed properties to string keys using a deterministic mapping (e.g. Symbol.keyFor).
- Strip symbol properties before canonicalizing if they are not part of the identity.
Example fix
// before
canonicalStringify({ a: 1, [Symbol('s')]: 2 })
// after
canonicalStringify({ a: 1 }) Defensive patterns
Strategy: validation
Validate before calling
function hasSymbolKeys(v: unknown): boolean {
if (v && typeof v === 'object') {
if (Object.getOwnPropertySymbols(v).length > 0) return true;
return Object.values(v).some(hasSymbolKeys);
}
return false;
} Type guard
function hasNoSymbolKeys(o: Record<string, unknown>): boolean {
return Object.getOwnPropertySymbols(o).length === 0;
} Try / catch
try {
canonicalStringify(value);
} catch (e) {
if (e instanceof TypeError && /symbol-keyed properties/.test(e.message)) {
// strip or stringify symbol keys and retry
}
throw e;
} Prevention
- Avoid symbol-keyed properties on objects used as cache keys.
- Project objects to string-keyed plain records before canonicalizing.
When it happens
Trigger: Passing a plain object that has at least one Symbol-typed key, e.g. { [Symbol('x')]: 1, a: 2 } or an object extended with symbol properties by a library.
Common situations: An ORM/library decorates objects with symbol-keyed metadata; using Symbol.for() as a property key for private-ish fields; caching a record that picked up symbol properties through spread/assign.
Related errors
- canonicalStringify: functions are not supported
- canonicalStringify: symbols are not supported
- canonicalStringify: non-plain objects are not supported (got
- canonicalStringify: circular reference detected
AI-assisted analysis of prisma/prisma@a20d61fb6f (2026-08-11).
Data as JSON: /api/errors/5ea7762495bd207a.
Report an issue: GitHub.