{"record":{"id":"4d6c940e77afa949","repo":"RocketChat/Rocket.Chat","slug":"error-custom-field-not-allowed","errorCode":"error-custom-field-not-allowed","errorMessage":"error-custom-field-not-allowed","messagePattern":"error-custom-field-not-allowed","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/lib/omnichannel/contacts/validateCustomFields.ts","lineNumber":50,"sourceCode":"\t\tif (cf.regexp) {\n\t\t\tconst regex = new RegExp(cf.regexp);\n\t\t\tif (!regex.test(cfValue)) {\n\t\t\t\tif (ignoreValidationErrors) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tthrow new Error(i18n.t('error-invalid-custom-field-value', { field: cf.label || cf._id }));\n\t\t\t}\n\t\t}\n\n\t\tvalidValues[cf._id] = cfValue;\n\t}\n\n\tif (!ignoreAdditionalFields) {\n\t\tconst allowedCustomFieldIds = new Set(allowedCustomFields.map((cf) => cf._id));\n\t\tfor (const key in customFields) {\n\t\t\tif (!allowedCustomFieldIds.has(key)) {\n\t\t\t\tthrow new Error(i18n.t('error-custom-field-not-allowed', { key }));\n\t\t\t}\n\t\t}\n\t}\n\n\treturn validValues;\n}\n","sourceCodeStart":32,"sourceCodeEnd":57,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/lib/omnichannel/contacts/validateCustomFields.ts#L32-L57","documentation":"After validating known fields, validateCustomFields walks every key of the submitted customFields object and rejects any key not present in allowedCustomFields when ignoreAdditionalFields is false. This is a closed-world check: the payload may only contain fields that are defined (and passed in as allowed) — unknown properties are an error, not ignored.","triggerScenarios":"Submitting customFields containing a key that is not a defined Livechat Custom Field id (typo, stale field, client-side bookkeeping property) through a code path that does not set ignoreAdditionalItems/ignoreAdditionalFields.","commonSituations":"A custom field was deleted server-side but old clients still send it; client injects metadata keys like '_updatedAt' or 'source' into the same object; copy-paste field id with different casing.","solutions":["Remove keys that are not defined custom-field ids, or create the missing field in Administration > Omnichannel > Custom Fields","Whitelist client-side: filter the payload to ids returned by GET /api/v1/livechat/custom-fields before submitting","For tolerant ingestion paths, use a code path that passes ignoreAdditionalFields: true"],"exampleFix":"// before\ncustomFields: { company: 'ACME', internal_note: 'x' } // 'internal_note' is not a defined field\n\n// after\nconst defined = new Set((await LivechatCustomField.find().toArray()).map((f) => f._id));\ncustomFields = Object.fromEntries(Object.entries(customFields).filter(([k]) => defined.has(k)));","handlingStrategy":"validation","validationCode":"const allowed = new Set((await LivechatCustomField.find().toArray()).map((f) => f._id));\nconst filtered = Object.fromEntries(Object.entries(customFields).filter(([k]) => allowed.has(k)));\n// pass `filtered` instead of the raw payload","typeGuard":"const containsOnlyAllowedKeys = (payload: Record<string, unknown>, allowed: Set<string>): boolean =>\n  Object.keys(payload).every((k) => allowed.has(k));","tryCatchPattern":"try {\n  await registerContact(params, userId);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('error-custom-field-not-allowed')) {\n    // message names the offending key: remove it or define the field, then retry\n  }\n  throw err;\n}","preventionTips":["Never reuse the customFields object to carry client metadata","Filter payloads against the live field list before submit","When deleting a custom field, clean up clients/configs that still emit it"],"tags":["omnichannel","livechat","custom-fields","unknown-field","validation","whitelist"],"backgroundTag":"unknown-field-rejected","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}