hcengineering/platform · error · PlatformError

Map is not allowed in model

Error message

Map is not allowed in model

What it means

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.

Source

Thrown at foundations/core/packages/core/src/proxy.ts:36

      if (property === PROXY_MIXIN_CLASS_KEY) {
        return mixin._id
      }
      const value = target[mixin._id]?.[property]
      if (value === undefined) {
        return ancestorProxy !== null ? ancestorProxy.get?.(target, property, receiver) : target[property]
      }
      return value
    }
  }
}

export function freeze (value: any): any {
  if (value != null && typeof value === 'object') {
    if (Array.isArray(value)) {
      return value.map((it) => freeze(it))
    }
    if (value instanceof Map) {
      throw new PlatformError(unknownError('Map is not allowed in model'))
    }
    if (value instanceof Set) {
      throw new PlatformError(unknownError('Set is not allowed in model'))
    }
    return new Proxy(value, _createFreezeProxy(value))
  }
  return value
}
/**
 * @internal
 */
export function _createFreezeProxy (doc: Doc): ProxyHandler<Doc> {
  return {
    get (target: any, property: string, receiver: any): any {
      const value = target[property]
      return freeze(value)
    },
    set (target, p, newValue, receiver): any {

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Replace the Map with a plain Record/object: Object.fromEntries(map) when writing the field.
  2. Keep Maps outside the Doc model (module-level caches, WeakMaps) and store only serializable data in docs.
  3. Convert at the boundary: when importing data, normalize Maps to plain objects before saving.

Example fix

// before
const doc = { palette: new Map([['white', '#fff']]) }
// after
const doc = { palette: Object.fromEntries([['white', '#fff']]) }
Defensive patterns

Strategy: validation

Validate before calling

function assertModelSafe(value: unknown, depth = 0): void {
  if (value == null || typeof value !== 'object' || depth > 8) return
  if (value instanceof Map) throw new TypeError('Map is not allowed in model; use a plain object')
  if (value instanceof Set) throw new TypeError('Set is not allowed in model; use an array')
  if (Array.isArray(value)) { value.forEach((v) => assertModelSafe(v, depth + 1)); return }
  for (const v of Object.values(value)) assertModelSafe(v, depth + 1)
}
assertModelSafe(docBeforeSave)

Type guard

function isPlainObject(v: unknown): v is Record<string, unknown> {
  if (v == null || typeof v !== 'object') return false
  const proto = Object.getPrototypeOf(v)
  return proto === Object.prototype || proto === null
}

Try / catch

try {
  const data = modelDoc.someField
  use(data)
} catch (e) {
  if (e instanceof PlatformError && e.message.includes('Map is not allowed in model')) {
    console.error('A Map leaked into the model; convert with Object.fromEntries(map) before storing.')
  } else throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/9961991ba9cb26aa. Report an issue: GitHub.