RocketChat/Rocket.Chat · error · Error

Attempted to store an invalid data type, it must be an objec

Error message

Attempted to store an invalid data type, it must be an object.

What it means

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.

Source

Thrown at apps/meteor/app/apps/server/bridges/persistence.ts:21

import type { RocketChatAssociationRecord } from '@rocket.chat/apps-engine/definition/metadata';
import type { InsertOneResult } from 'mongodb';

export class AppPersistenceBridge extends PersistenceBridge {
	constructor(private readonly orch: IAppServerOrchestrator) {
		super();
	}

	protected async purge(appId: string): Promise<void> {
		this.orch.debugLog(`The App's persistent storage is being purged: ${appId}`);

		await this.orch.getPersistenceModel().remove({ appId });
	}

	protected async create(data: object, appId: string): Promise<string> {
		this.orch.debugLog(`The App ${appId} is storing a new object in their persistence.`);

		if (typeof data !== 'object') {
			throw new Error('Attempted to store an invalid data type, it must be an object.');
		}

		return this.orch
			.getPersistenceModel()
			.insertOne({ appId, data })
			.then(({ insertedId }: InsertOneResult) => (insertedId as unknown as string) || '');
	}

	protected async createWithAssociations(data: object, associations: Array<RocketChatAssociationRecord>, appId: string): Promise<string> {
		this.orch.debugLog({
			msg: `The App ${appId} is storing a new object in their persistence that is associated with some models.`,
			associations,
		});

		if (typeof data !== 'object') {
			throw new Error('Attempted to store an invalid data type, it must be an object.');
		}

View on GitHub (pinned to f9d3ec372b)

Solutions

  1. Pass a plain object: wrap scalars, e.g. `{ value: count }` instead of the bare number.
  2. If receiving serialized data, parse it first: `const data = JSON.parse(raw); if (typeof data !== 'object') ...`.
  3. Avoid null and arrays if you expect a record shape — add your own Array.isArray / null check before calling.

Example fix

// before
await persistence.create(JSON.stringify(payload), appId);

// after
await persistence.create(payload, appId);
Defensive patterns

Strategy: type-guard

Validate before calling

if (data === null || data === undefined || typeof data !== 'object' || Array.isArray(data)) {
  throw new Error('persistence.create requires a plain object');
}
await persistence.create(data, appId);

Type guard

function isPlainObject(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value);
}

Prevention

When it happens

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

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

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@f9d3ec372b (2026-08-12). Data as JSON: /api/errors/0c37e548ca89e698. Report an issue: GitHub.