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
- Build the param programmatically: `custom=${encodeURIComponent(JSON.stringify({ key: 'value' }))}` so keys and values are double-quoted and encoded.
- Use the dedicated ?email= and ?phone= params for plain email/phone lookups; keep custom only for custom-field criteria.
- 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
- Always JSON.stringify the custom object and let the HTTP client URL-encode it.
- Never hand-type the custom param in cURL/Postman; use an encoder.
- Put plain email/phone in their dedicated params.
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
- The "customFields" query parameter must be a valid JSON.
- error-invalid-params
- error-token-param-not-provided
- Invalid custom fields
- The " .end" query parameter must be a valid date.
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)