{"record":{"id":"7bcb73c488874bd0","repo":"RocketChat/Rocket.Chat","slug":"error-invalid-params-custom","errorCode":"error-invalid-params-custom","errorMessage":"error-invalid-params-custom","messagePattern":"error-invalid-params-custom","errorType":"error_code","errorClass":"Meteor.Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/api/v1/omnichannel/contact.ts","lineNumber":89,"sourceCode":"\t'omnichannel/contact.search',\n\t{\n\t\tauthRequired: true,\n\t\tpermissionsRequired: ['view-l-room'],\n\t},\n\t{\n\t\tasync get() {\n\t\t\tcheck(this.queryParams, {\n\t\t\t\temail: Match.Maybe(String),\n\t\t\t\tphone: Match.Maybe(String),\n\t\t\t\tcustom: Match.Maybe(String),\n\t\t\t});\n\t\t\tconst { email, phone, custom } = this.queryParams;\n\n\t\t\tlet customCF: { [k: string]: string } = {};\n\t\t\ttry {\n\t\t\t\tcustomCF = custom && JSON.parse(custom);\n\t\t\t} catch (e) {\n\t\t\t\tthrow new Meteor.Error('error-invalid-params-custom');\n\t\t\t}\n\n\t\t\tif (!email && !phone && !Object.keys(customCF).length) {\n\t\t\t\tthrow new Meteor.Error('error-invalid-params');\n\t\t\t}\n\n\t\t\tconst foundCF = await (async () => {\n\t\t\t\tif (!custom) {\n\t\t\t\t\treturn {};\n\t\t\t\t}\n\n\t\t\t\tconst cfIds = Object.keys(customCF);\n\n\t\t\t\tconst customFields = await LivechatCustomField.findMatchingCustomFieldsByIds(cfIds, 'visitor', true, {\n\t\t\t\t\tprojection: { _id: 1 },\n\t\t\t\t}).toArray();\n\n\t\t\t\treturn Object.fromEntries(customFields.map(({ _id }) => [`livechatData.${_id}`, new RegExp(escapeRegExp(customCF[_id]), 'i')]));","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/api/v1/omnichannel/contact.ts#L71-L107","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nfetch(`/api/v1/livechat/contact?custom={twitter:@user}`); // invalid JSON -> error-invalid-params-custom\n\n// after\nconst custom = encodeURIComponent(JSON.stringify({ twitter: '@user' }));\nfetch(`/api/v1/livechat/contact?custom=${custom}`);","handlingStrategy":"validation","validationCode":"function buildContactQuery(custom: Record<string, string>, email?: string, phone?: string): string | null {\n  const qs = new URLSearchParams();\n  if (email) qs.set('email', email);\n  if (phone) qs.set('phone', phone);\n  if (custom && Object.keys(custom).length) {\n    const encoded = JSON.stringify(custom); // must parse\n    JSON.parse(encoded); // local sanity check\n    qs.set('custom', encoded);\n  }\n  return qs.toString() || null;\n}","typeGuard":"function isJsonObjectString(v: string): boolean {\n  try { const parsed = JSON.parse(v); return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed); } catch { return false; }\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["omnichannel","contact","query-params","json","rest-api"],"backgroundTag":"json-parse-error","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"}