{"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":null,"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/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/app/apps/server/bridges/persistence.ts#L3-L39","documentation":"AppPersistenceBridge.create stores documents as { appId, data } in the app persistence collection and enforces typeof data === 'object' before inserting. Primitives (string, number, boolean, undefined, function) are rejected with this Error. Note the guard's blind spots: typeof null === 'object' lets null through, and arrays also pass.","triggerScenarios":"Calling persistence.create(JSON.stringify(payload)) (a string); passing a number/boolean flag; passing undefined because the variable or property was misspelled or never set.","commonSituations":"Developers serializing before storing out of habit; migrating code that stored scalars; optional values that silently resolve to undefined; test data built inline as strings.","solutions":["Store a plain object — wrap scalars as { value: someScalar } if you only need one","If you have a JSON string, JSON.parse it before create","Double-check property names/typos that produce undefined","For associated storage, the same rule applies to createWithAssociations"],"exampleFix":"// before\nawait app.getPersistence().create(JSON.stringify({ status: 'done' })); // string -> throws\n// after\nawait app.getPersistence().create({ status: 'done' });","handlingStrategy":"type-guard","validationCode":"const data = JSON.parse(rawJson) as Record<string, unknown>;\nawait app.getPersistence().create(data);","typeGuard":"const isPersistableObject = (d: unknown): d is Record<string, unknown> =>\n  typeof d === 'object' && d !== null && !Array.isArray(d);\n\nif (!isPersistableObject(payload)) throw new Error('persistence payload must be a non-null object');\nawait app.getPersistence().create(payload);","tryCatchPattern":"try {\n  await app.getPersistence().create(payload);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('invalid data type')) {\n    throw new Error(`persistence.create received ${typeof payload} — wrap primitives in an object`);\n  }\n  throw err;\n}","preventionTips":["Never JSON.stringify before persistence — the bridge stores objects natively","Wrap scalars: store { value } rather than a raw token/number","Type persistence helpers as (data: Record<string, unknown>) so strings fail to compile","Beware the guard's blind spots: null and arrays pass typeof === 'object'"],"tags":["apps-engine","persistence","type-validation","data-type"],"backgroundTag":"schema-validation-failed","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}