hcengineering/platform · error · PlatformError
Modification is not allowed
Error message
Modification is not allowed
What it means
Model documents are handed out as read-only freeze proxies; the proxy's set trap unconditionally throws this PlatformError on any property assignment. The model is immutable from the client's perspective — mutations must go through transactions/update APIs, never direct property writes. Note that errors thrown inside a Proxy set trap surface at the assignment site, which may be far from the model code.
Source
Thrown at foundations/core/packages/core/src/proxy.ts:55
}
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 {
throw new PlatformError(unknownError('Modification is not allowed'))
},
defineProperty (target, property, attributes): any {
throw new PlatformError(unknownError('Modification is not allowed'))
},
deleteProperty (target, p): any {
throw new PlatformError(unknownError('Modification is not allowed'))
},
setPrototypeOf (target, v): any {
throw new PlatformError(unknownError('Modification is not allowed'))
}
}
}
/**
* @internal
*/
export function _toDoc<D extends Doc> (doc: D): D {View on GitHub (pinned to 63e28dc964)
Solutions
- Use the library's update/transaction API ($set) to change document data instead of direct assignment.
- Unwrap the proxy with _toDoc(doc) only if you need to read internals — but perform writes through the proper API.
- Build a new plain object (spread) for derived/computed results instead of mutating the frozen doc.
- Clone fields you need to modify: const copy = { ...toDoc(doc) }; copy.field = x; then persist copy via update.
Example fix
// before
doc.title = 'new title'
await tx.updateDoc(doc)
// after
await tx.updateDoc(_class, _id, _id, { $set: { title: 'new title' } }) Defensive patterns
Strategy: try-catch
Validate before calling
function isFrozenModelDoc(v: unknown): boolean {
return v != null && typeof v === 'object' && (v as any).$___proxy_target !== undefined
}
if (!isFrozenModelDoc(doc)) {
// safe-ish plain object; still prefer the update API for persistence
} Type guard
function isMutableCopy<T extends object>(v: T): v is T & { __mutable: true } {
return Object.isFrozen(v) === false && (v as any).$___proxy_target === undefined
} Try / catch
try {
(doc as any).title = 'x'
} catch (e) {
if (e instanceof PlatformError && e.message.includes('Modification is not allowed')) {
await tx.updateDoc(_class, _id, _id, { $set: { title: 'x' } })
} else throw e
} Prevention
- Treat all query/model results as immutable; never assign to their properties.
- Use the update API ($set) for every mutation.
- Create plain-object copies ({ ...toDoc(doc) }) for computed or grouped results.
- Avoid Object.assign on model docs — it triggers the same proxy traps.
When it happens
Trigger: Writing doc.someField = value on any object obtained from the model/query results (e.g. inside helpers like groupByArray or while processing queried docs), or via Object.assign(doc, {...}) and destructuring-with-reassignment on a frozen doc.
Common situations: Mutating a fetched document in place before saving; augmenting query results with computed fields; cache-style code assuming plain mutable objects; copying patterns that rely on assignment into the same object.
Related errors
- Map is not allowed in model
- Set is not allowed in model
- ancestors not found: ${_class}
- class not found: ${_class}
- Method '${methodName}' not found in service implementation
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/49d46635778cca62.
Report an issue: GitHub.