{"record":{"id":"9961991ba9cb26aa","repo":"hcengineering/platform","slug":"map-is-not-allowed-in-model","errorCode":null,"errorMessage":"Map is not allowed in model","messagePattern":"Map is not allowed in model","errorType":"exception","errorClass":"PlatformError","httpStatus":null,"severity":"error","filePath":"foundations/core/packages/core/src/proxy.ts","lineNumber":36,"sourceCode":"      if (property === PROXY_MIXIN_CLASS_KEY) {\n        return mixin._id\n      }\n      const value = target[mixin._id]?.[property]\n      if (value === undefined) {\n        return ancestorProxy !== null ? ancestorProxy.get?.(target, property, receiver) : target[property]\n      }\n      return value\n    }\n  }\n}\n\nexport function freeze (value: any): any {\n  if (value != null && typeof value === 'object') {\n    if (Array.isArray(value)) {\n      return value.map((it) => freeze(it))\n    }\n    if (value instanceof Map) {\n      throw new PlatformError(unknownError('Map is not allowed in model'))\n    }\n    if (value instanceof Set) {\n      throw new PlatformError(unknownError('Set is not allowed in model'))\n    }\n    return new Proxy(value, _createFreezeProxy(value))\n  }\n  return value\n}\n/**\n * @internal\n */\nexport function _createFreezeProxy (doc: Doc): ProxyHandler<Doc> {\n  return {\n    get (target: any, property: string, receiver: any): any {\n      const value = target[property]\n      return freeze(value)\n    },\n    set (target, p, newValue, receiver): any {","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/core/packages/core/src/proxy.ts#L18-L54","documentation":"The library's model/Doc values are exposed as immutable freeze proxies, and freeze() recursively wraps values it returns. JavaScript Map instances cannot be wrapped by this proxy mechanism (per-field model values must be plain serializable data), so encountering a Map throws a PlatformError. It signals that a non-plain-data structure leaked into the model.","triggerScenarios":"Storing or returning a Map as a document field value, e.g. { data: new Map([['a', 1]]) } in a Doc, then reading that doc through the frozen proxy (any get on a model object that returns the Map). Callers include attribute getters like whitePalette/avatarWhiteColors that read model objects.","commonSituations":"Using Map for convenience in domain classes attached to docs; deserializing data that came back as Map (some YAML/ORM libs); upgrading code where a field type changed from plain object to Map.","solutions":["Replace the Map with a plain Record/object: Object.fromEntries(map) when writing the field.","Keep Maps outside the Doc model (module-level caches, WeakMaps) and store only serializable data in docs.","Convert at the boundary: when importing data, normalize Maps to plain objects before saving."],"exampleFix":"// before\nconst doc = { palette: new Map([['white', '#fff']]) }\n// after\nconst doc = { palette: Object.fromEntries([['white', '#fff']]) }","handlingStrategy":"validation","validationCode":"function assertModelSafe(value: unknown, depth = 0): void {\n  if (value == null || typeof value !== 'object' || depth > 8) return\n  if (value instanceof Map) throw new TypeError('Map is not allowed in model; use a plain object')\n  if (value instanceof Set) throw new TypeError('Set is not allowed in model; use an array')\n  if (Array.isArray(value)) { value.forEach((v) => assertModelSafe(v, depth + 1)); return }\n  for (const v of Object.values(value)) assertModelSafe(v, depth + 1)\n}\nassertModelSafe(docBeforeSave)","typeGuard":"function isPlainObject(v: unknown): v is Record<string, unknown> {\n  if (v == null || typeof v !== 'object') return false\n  const proto = Object.getPrototypeOf(v)\n  return proto === Object.prototype || proto === null\n}","tryCatchPattern":"try {\n  const data = modelDoc.someField\n  use(data)\n} catch (e) {\n  if (e instanceof PlatformError && e.message.includes('Map is not allowed in model')) {\n    console.error('A Map leaked into the model; convert with Object.fromEntries(map) before storing.')\n  } else throw e\n}","preventionTips":["Only store JSON-serializable plain objects, arrays, and primitives in doc fields.","Convert Maps with Object.fromEntries at the write boundary.","Add a deep model-safety assertion in tests for domain classes attached to docs."],"tags":["model","proxy","immutability"],"backgroundTag":"map-not-allowed-in-model","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}