RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-params-custom

error-invalid-params-custom

Error message

error-invalid-params-custom

What it means

GET /api/v1/livechat/contact reads query params email, phone and custom. The custom param must be a URL-encoded JSON object string; when custom is non-empty and JSON.parse(custom) throws, the endpoint fails with Meteor.Error 'error-invalid-params-custom'.

Solutions

  1. Build the param programmatically: `custom=${encodeURIComponent(JSON.stringify({ key: 'value' }))}` so keys and values are double-quoted and encoded.
  2. Use the dedicated ?email= and ?phone= params for plain email/phone lookups; keep custom only for custom-field criteria.
  3. Log the decoded custom value server-side/client-side and re-run JSON.parse locally to spot the exact syntax break.

Example fix

// before
fetch(`/api/v1/livechat/contact?custom={twitter:@user}`); // invalid JSON -> error-invalid-params-custom

// after
const custom = encodeURIComponent(JSON.stringify({ twitter: '@user' }));
fetch(`/api/v1/livechat/contact?custom=${custom}`);
Defensive patterns

Strategy: validation

Validate before calling

function buildContactQuery(custom: Record<string, string>, email?: string, phone?: string): string | null {
  const qs = new URLSearchParams();
  if (email) qs.set('email', email);
  if (phone) qs.set('phone', phone);
  if (custom && Object.keys(custom).length) {
    const encoded = JSON.stringify(custom); // must parse
    JSON.parse(encoded); // local sanity check
    qs.set('custom', encoded);
  }
  return qs.toString() || null;
}

Type guard

function isJsonObjectString(v: string): boolean {
  try { const parsed = JSON.parse(v); return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); } catch { return false; }
}

Prevention

When it happens

Trigger: Passing a raw value like ?custom=someone@example.com instead of JSON; hand-built query strings with unquoted keys ({twitter: @user}); JSON containing single quotes or unencoded braces/quotes that break URL parsing; forgetting to JSON.stringify+encodeURIComponent the object.

Common situations: Developers putting a bare email into custom instead of the dedicated email param; cURL/Postman queries typed by hand without URL encoding; integrations concatenating strings instead of using a query-string serializer.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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

Appendix: source

Thrown at apps/meteor/server/api/v1/omnichannel/contact.ts:89

	'omnichannel/contact.search',
	{
		authRequired: true,
		permissionsRequired: ['view-l-room'],
	},
	{
		async get() {
			check(this.queryParams, {
				email: Match.Maybe(String),
				phone: Match.Maybe(String),
				custom: Match.Maybe(String),
			});
			const { email, phone, custom } = this.queryParams;

			let customCF: { [k: string]: string } = {};
			try {
				customCF = custom && JSON.parse(custom);
			} catch (e) {
				throw new Meteor.Error('error-invalid-params-custom');
			}

			if (!email && !phone && !Object.keys(customCF).length) {
				throw new Meteor.Error('error-invalid-params');
			}

			const foundCF = await (async () => {
				if (!custom) {
					return {};
				}

				const cfIds = Object.keys(customCF);

				const customFields = await LivechatCustomField.findMatchingCustomFieldsByIds(cfIds, 'visitor', true, {
					projection: { _id: 1 },
				}).toArray();

				return Object.fromEntries(customFields.map(({ _id }) => [`livechatData.${_id}`, new RegExp(escapeRegExp(customCF[_id]), 'i')]));

View on GitHub (pinned to b2c16d5842)