{"record":{"id":"d9a4a734045e3607","repo":"RocketChat/Rocket.Chat","slug":"username-required","errorCode":null,"errorMessage":"username-required","messagePattern":"username-required","errorType":"exception","errorClass":"Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/api/webhooks.ts","lineNumber":86,"sourceCode":"\t\t\t\turls: [options.target_url],\n\t\t\t\tname: options.name,\n\t\t\t\tchannel: options.data.channel_name,\n\t\t\t\ttriggerWords: options.data.trigger_words,\n\t\t\t\ttype: 'webhook-outgoing',\n\t\t\t\tevent: 'sendMessage',\n\t\t\t\ttoken: Random.id(24),\n\t\t\t\tscriptEnabled: false,\n\t\t\t\tscript: '',\n\t\t\t\tenabled: true,\n\t\t\t\t_id: Random.id(),\n\t\t\t\t_updatedAt: new Date(),\n\t\t\t});\n\t\tcase 'newMessageToUser':\n\t\t\tif (options.data?.username?.indexOf('@') === -1) {\n\t\t\t\toptions.data.username = `@${options.data.username}`;\n\t\t\t}\n\t\t\tif (!options.data?.username) {\n\t\t\t\tthrow new Error('username-required');\n\t\t\t}\n\n\t\t\treturn addOutgoingIntegration(user._id, {\n\t\t\t\tusername: 'rocket.cat',\n\t\t\t\turls: [options.target_url],\n\t\t\t\tname: options.name,\n\t\t\t\tchannel: options.data.username,\n\t\t\t\ttriggerWords: options.data.trigger_words,\n\t\t\t\t_id: '',\n\t\t\t\ttype: 'webhook-outgoing',\n\t\t\t\ttoken: '',\n\t\t\t\tscriptEnabled: false,\n\t\t\t\tscript: '',\n\t\t\t\tenabled: false,\n\t\t\t\t_updatedAt: new Date(),\n\t\t\t\tevent: 'sendMessage',\n\t\t\t});\n\t}","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/api/webhooks.ts#L68-L104","documentation":"Thrown by POST /hooks/add/:integrationId/:userId/:token (legacy Zapier-style integration webhook) when event is 'newMessageToUser' but bodyParams.data.username is missing or empty. The outgoing integration needs a @username channel to post into, so creation aborts with the plain error 'username-required'. Note the code checks the '@' prefix before existence, so a missing username always falls through to this throw.","triggerScenarios":"POST to /api/v1/hooks/add/... with {event:'newMessageToUser', name:'x', target_url:'https://cb'} and no data object; data:{trigger_words:[...]} without username; username:'' (empty string gets auto-prefixed to '@' and behaves unexpectedly — always send a real username).","commonSituations":"Zapier-style flows copied from the channel template (newMessageOnChannel uses channel_name) without swapping to username; form integrations with optional username left blank; version drift in a fields mapper dropping the username key.","solutions":["Include data.username (bare username is fine; server prepends '@') in the POST body for newMessageToUser events","Switch the event to newMessageOnChannel and supply data.channel_name if you meant a channel","Validate the payload shape before firing the webhook (event-specific required keys)","Check the integration webhook logs (incomingLogger) to confirm which branch ran"],"exampleFix":"// before\nawait fetch(`${rc}/api/v1/hooks/add/${iid}/${uid}/${tok}`, { method: 'POST', body: JSON.stringify({ event: 'newMessageToUser', name: 'zap', target_url: 'https://cb.example' }) });\n// after\nawait fetch(`${rc}/api/v1/hooks/add/${iid}/${uid}/${tok}`, { method: 'POST', body: JSON.stringify({ event: 'newMessageToUser', name: 'zap', target_url: 'https://cb.example', data: { username: 'jon.doe' } }) });","handlingStrategy":"validation","validationCode":"function validateHooksAddPayload(p: {event:string; name:string; target_url:string; data?:{channel_name?:string; username?:string}}) {\n  if (p.event === 'newMessageToUser' && !p.data?.username) throw new Error('username-required: data.username missing');\n  if (p.event === 'newMessageOnChannel' && !p.data?.channel_name) throw new Error('channel_name required');\n}","typeGuard":"const isMessageToUserPayload = (p: unknown): p is {event:'newMessageToUser'; name:string; target_url:string; data:{username:string}} =>\n  typeof p === 'object' && p !== null && (p as {event?:string}).event === 'newMessageToUser' && typeof ((p as {data?:{username?:string}}).data)?.username === 'string' && ((p as {data:{username:string}}).data.username.length > 0);","tryCatchPattern":"try { await postHooksAdd(payload); } catch (e) { if (/username-required/.test(String(e?.message))) { payload.data = { ...payload.data, username: fallbackUser }; await postHooksAdd(payload); } else throw e; }","preventionTips":["Map required fields per event type before building the payload","Never send empty-string username (server auto-prefixes '@')","Reuse the channel template only with newMessageOnChannel"],"tags":["webhooks","integrations","validation","outgoing-integration"],"backgroundTag":"missing-required-argument","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"}