{"record":{"id":"5ea7762495bd207a","repo":"prisma/prisma","slug":"canonicalstringify-objects-with-symbol-keyed-prop","errorCode":null,"errorMessage":"canonicalStringify: objects with symbol-keyed properties are not supported","messagePattern":"canonicalStringify: objects with symbol-keyed properties are not supported","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/1-framework/0-foundation/utils/src/canonical-stringify.ts","lineNumber":138,"sourceCode":"}\n\nfunction writePlainObject(obj: Record<string, unknown>, seen: Set<object>): string {\n  // Only true plain objects are accepted here. Without this guard, anything\n  // that fell through the type-tagged branches above (`Map`, `Set`,\n  // `RegExp`, class instances, …) would canonicalize to `{}` because\n  // `Object.keys` returns no enumerable string keys for them — silently\n  // colliding with each other and with the literal `{}`.\n  const proto = Object.getPrototypeOf(obj);\n  if (proto !== Object.prototype && proto !== null) {\n    const tag = proto?.constructor?.name ?? 'unknown';\n    throw new TypeError(`canonicalStringify: non-plain objects are not supported (got ${tag})`);\n  }\n\n  // `Object.keys` ignores symbol-keyed properties, so they would be\n  // silently dropped from the canonical form. Force callers to handle\n  // them explicitly instead of producing a key that omits real data.\n  if (Object.getOwnPropertySymbols(obj).length > 0) {\n    throw new TypeError(\n      'canonicalStringify: objects with symbol-keyed properties are not supported',\n    );\n  }\n\n  const keys = Object.keys(obj).sort();\n  const parts: string[] = [];\n  for (const key of keys) {\n    parts.push(`${JSON.stringify(key)}:${write(obj[key], seen)}`);\n  }\n  return `{${parts.join(',')}}`;\n}\n\nfunction bytesToHex(bytes: Uint8Array): string {\n  let out = '';\n  for (let i = 0; i < bytes.length; i++) {\n    const byte = bytes[i] as number;\n    out += byte.toString(16).padStart(2, '0');\n  }","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/prisma/prisma/blob/a20d61fb6f095e636e6420ac2871c24f2dece4bb/packages/1-framework/0-foundation/utils/src/canonical-stringify.ts#L120-L156","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ncanonicalStringify({ a: 1, [Symbol('s')]: 2 })\n// after\ncanonicalStringify({ a: 1 })","handlingStrategy":"validation","validationCode":"function hasSymbolKeys(v: unknown): boolean {\n  if (v && typeof v === 'object') {\n    if (Object.getOwnPropertySymbols(v).length > 0) return true;\n    return Object.values(v).some(hasSymbolKeys);\n  }\n  return false;\n}","typeGuard":"function hasNoSymbolKeys(o: Record<string, unknown>): boolean {\n  return Object.getOwnPropertySymbols(o).length === 0;\n}","tryCatchPattern":"try {\n  canonicalStringify(value);\n} catch (e) {\n  if (e instanceof TypeError && /symbol-keyed properties/.test(e.message)) {\n    // strip or stringify symbol keys and retry\n  }\n  throw e;\n}","preventionTips":["Avoid symbol-keyed properties on objects used as cache keys.","Project objects to string-keyed plain records before canonicalizing."],"tags":["canonical-stringify","symbol-keys","unsupported-type","cache-key"],"backgroundTag":null,"analyzedSha":"a20d61fb6f095e636e6420ac2871c24f2dece4bb","analyzedAt":"2026-08-11T15:42:48.797Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}