{"record":{"id":"0c37e548ca89e698","repo":"RocketChat/Rocket.Chat","slug":"attempted-to-store-an-invalid-data-type-it-must-b","errorCode":null,"errorMessage":"Attempted to store an invalid data type, it must be an object.","messagePattern":"Attempted to store an invalid data type, it must be an object\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/app/apps/server/bridges/persistence.ts","lineNumber":21,"sourceCode":"import type { RocketChatAssociationRecord } from '@rocket.chat/apps-engine/definition/metadata';\nimport type { InsertOneResult } from 'mongodb';\n\nexport class AppPersistenceBridge extends PersistenceBridge {\n\tconstructor(private readonly orch: IAppServerOrchestrator) {\n\t\tsuper();\n\t}\n\n\tprotected async purge(appId: string): Promise<void> {\n\t\tthis.orch.debugLog(`The App's persistent storage is being purged: ${appId}`);\n\n\t\tawait this.orch.getPersistenceModel().remove({ appId });\n\t}\n\n\tprotected async create(data: object, appId: string): Promise<string> {\n\t\tthis.orch.debugLog(`The App ${appId} is storing a new object in their persistence.`);\n\n\t\tif (typeof data !== 'object') {\n\t\t\tthrow new Error('Attempted to store an invalid data type, it must be an object.');\n\t\t}\n\n\t\treturn this.orch\n\t\t\t.getPersistenceModel()\n\t\t\t.insertOne({ appId, data })\n\t\t\t.then(({ insertedId }: InsertOneResult) => (insertedId as unknown as string) || '');\n\t}\n\n\tprotected async createWithAssociations(data: object, associations: Array<RocketChatAssociationRecord>, appId: string): Promise<string> {\n\t\tthis.orch.debugLog({\n\t\t\tmsg: `The App ${appId} is storing a new object in their persistence that is associated with some models.`,\n\t\t\tassociations,\n\t\t});\n\n\t\tif (typeof data !== 'object') {\n\t\t\tthrow new Error('Attempted to store an invalid data type, it must be an object.');\n\t\t}\n","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/app/apps/server/bridges/persistence.ts#L3-L39","documentation":"Thrown by AppPersistenceBridge.create when the data argument fails `typeof data !== 'object'`. The persistence layer stores arbitrary app data as a Mongo document; non-object payloads (string, number, boolean, undefined) cannot be persisted as a sub-document and are rejected. Note the guard uses typeof, so null passes (typeof null === 'object') and arrays also pass — both will be stored as-is, which may not be intended.","triggerScenarios":"An App calls persistence.create(data) (via the persistence accessor) passing a primitive: a JSON string instead of a parsed object, a number counter, a boolean flag, or undefined because the variable was never assigned.","commonSituations":"Forgetting to JSON.parse a serialized payload before persisting; storing a scalar where an object was expected; refactoring that changed a value from object to primitive without updating the persistence call.","solutions":["Pass a plain object: wrap scalars, e.g. `{ value: count }` instead of the bare number.","If receiving serialized data, parse it first: `const data = JSON.parse(raw); if (typeof data !== 'object') ...`.","Avoid null and arrays if you expect a record shape — add your own Array.isArray / null check before calling."],"exampleFix":"// before\nawait persistence.create(JSON.stringify(payload), appId);\n\n// after\nawait persistence.create(payload, appId);","handlingStrategy":"type-guard","validationCode":"if (data === null || data === undefined || typeof data !== 'object' || Array.isArray(data)) {\n  throw new Error('persistence.create requires a plain object');\n}\nawait persistence.create(data, appId);","typeGuard":"function isPlainObject(value: unknown): value is Record<string, unknown> {\n  return typeof value === 'object' && value !== null && !Array.isArray(value);\n}","tryCatchPattern":null,"preventionTips":["Never pass a serialized JSON string — pass the parsed object.","Wrap scalars in a record ({ value: n }) before persisting.","Add an isPlainObject guard at your App's persistence wrapper so the bridge never sees a bad payload."],"tags":["apps-engine","persistence-bridge","validation","data-type","typeof"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}