RocketChat/Rocket.Chat · error · Error

Missing required custom fields: ${errors.join(', ')}

Error message

Missing required custom fields: ${errors.join(', ')}

What it means

validateRequiredCustomFields (used by setMultipleVisitorCustomFields before writing visitor/room custom fields) compares the list of field keys you are about to set against every field marked required in the definition. If any required field id is absent from your keys array, it throws a plain Error listing the missing ids in the message — note it has no error code, only the interpolated text.

Source

Thrown at apps/meteor/server/lib/omnichannel/custom-fields.ts:18

import type { ILivechatContact, ILivechatCustomField, ILivechatVisitor } from '@rocket.chat/core-typings';
import { LivechatContacts, LivechatCustomField, LivechatRooms, LivechatVisitors } from '@rocket.chat/models';

import { livechatLogger } from './logger';
import { i18n } from '../../../app/utils/lib/i18n';

export const validateRequiredCustomFields = (customFields: string[], livechatCustomFields: ILivechatCustomField[]) => {
	const errors: string[] = [];
	const requiredCustomFields = livechatCustomFields.filter((field) => field.required);

	requiredCustomFields.forEach((field) => {
		if (!customFields.find((f) => f === field._id)) {
			errors.push(field._id);
		}
	});

	if (errors.length > 0) {
		throw new Error(`Missing required custom fields: ${errors.join(', ')}`);
	}
};

export async function updateContactsCustomFields(
	contact: ILivechatContact,
	validCustomFields: {
		key: string;
		value: string;
		overwrite: boolean;
	}[],
): Promise<void> {
	const contactCustomFieldsToUpdate = validCustomFields.reduce(
		(prev, curr) => {
			if (curr.overwrite || !contact?.customFields?.[curr.key]) {
				prev[`customFields.${curr.key}`] = curr.value;
				return prev;
			}
			prev.conflictingFields ??= contact.conflictingFields || [];

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Include every required field id in the customFields array — the error message names exactly which ids are missing
  2. Make the field optional in Administration > Omnichannel > Custom Fields if it should not block writes
  3. Fetch required ids at runtime (GET /api/v1/livechat/custom-fields, filter required) instead of hardcoding the list

Example fix

// before
await setMultipleVisitorCustomFields({ token, customFields: [{ key: 'company', value: 'ACME', overwrite: true }] }); // 'address' required

// after
await setMultipleVisitorCustomFields({ token, customFields: [
  { key: 'company', value: 'ACME', overwrite: true },
  { key: 'address', value: '1 Main St', overwrite: true },
] });
Defensive patterns

Strategy: validation

Validate before calling

const allFields = await LivechatCustomField.find().toArray();
const requiredIds = allFields.filter((f) => f.required).map((f) => f._id);
const missing = requiredIds.filter((id) => !keys.includes(id));
if (missing.length) throw new Error(`Missing required custom fields: ${missing.join(', ')}`);
await setMultipleVisitorCustomFields(params);

Type guard

const coversRequired = (keys: string[], required: string[]): boolean =>
  required.every((id) => keys.includes(id));

Try / catch

try {
  await setMultipleVisitorCustomFields(params);
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Missing required custom fields')) {
    // parse field ids from the message, add them with valid values, retry
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling setMultipleVisitorCustomFields (visitor registration/custom-field batch flows) with a keys list that covers only some fields, while at least one field in LivechatCustomField has required=true.

Common situations: New required field added by admins after the integration was written; webhook sends a dynamic subset of fields; required field hidden in the chat widget form.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/42893f2735c5d56e. Report an issue: GitHub.