{"record":{"id":"4c38746cbb92ff2c","repo":"RocketChat/Rocket.Chat","slug":"error-authtoken-param-not-valid","errorCode":"error-authToken-param-not-valid","errorMessage":"The required \"authToken\" header param is missing or invalid.","messagePattern":"The required \"authToken\" header param is missing or invalid\\.","errorType":"exception","errorClass":"Meteor.Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/server/api/v1/push.ts","lineNumber":176,"sourceCode":"\t\t\t\t\trequired: ['success', 'result'],\n\t\t\t\t}),\n\t\t\t\t400: validateBadRequestErrorResponse,\n\t\t\t\t401: validateUnauthorizedErrorResponse,\n\t\t\t\t403: validateForbiddenErrorResponse,\n\t\t\t},\n\t\t\tbody: isPushTokenPOSTProps,\n\t\t\tauthRequired: true,\n\t\t},\n\t\tasync function action() {\n\t\t\tconst { id, type, value, appName, voipToken } = this.bodyParams;\n\n\t\t\tif (voipToken && !id) {\n\t\t\t\treturn API.v1.failure('voip-tokens-must-specify-device-id');\n\t\t\t}\n\n\t\t\tconst rawToken = this.request.headers.get('x-auth-token');\n\t\t\tif (!rawToken) {\n\t\t\t\tthrow new Meteor.Error('error-authToken-param-not-valid', 'The required \"authToken\" header param is missing or invalid.');\n\t\t\t}\n\t\t\tconst authToken = Accounts._hashLoginToken(rawToken);\n\n\t\t\tconst result = await Push.registerPushToken({\n\t\t\t\t...(id && { _id: id }),\n\t\t\t\ttoken: { [type]: value } as IPushToken['token'],\n\t\t\t\tauthToken,\n\t\t\t\tappName,\n\t\t\t\tuserId: this.userId,\n\t\t\t\t...(voipToken && { voipToken }),\n\t\t\t});\n\n\t\t\treturn API.v1.success({ result: cleanTokenResult(result) });\n\t\t},\n\t)\n\t.delete(\n\t\t'push.token',\n\t\t{","sourceCodeStart":158,"sourceCodeEnd":194,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/f9d3ec372bb580fa8d036f94cf03925a478ef768/apps/meteor/server/api/v1/push.ts#L158-L194","documentation":"Thrown as Meteor.Error('error-authToken-param-not-valid', ...) in the POST push.token action when the x-auth-token request header is missing/null. The route declares authRequired:true (so this.userId is set), but the push token registration additionally needs the raw token to hash it via Accounts._hashLoginToken for device binding.","triggerScenarios":"POST /api/v1/v1/push.token without the x-auth-token header, or with an empty value. The body can be otherwise valid (isPushTokenPOSTProps passes).","commonSituations":"Mobile/SDK client set X-User-Id but forgot the matching X-Auth-Token header; a proxy stripping the header; the token was stored in a different key than x-auth-token.","solutions":["Include both X-User-Id and X-Auth-Token headers on the request (the same pair used for the session).","Verify the header name casing matches (HTTP headers are case-insensitive but the client must send the right name).","Check that the proxy/gateway does not strip x-auth-token."],"exampleFix":"// before\nfetch('/api/v1/push.token', { headers: { 'X-User-Id': uid }, body });\n\n// after\nfetch('/api/v1/push.token', { headers: { 'X-User-Id': uid, 'X-Auth-Token': rawToken }, body });","handlingStrategy":"validation","validationCode":"if (!rawToken) throw new Error('x-auth-token header required');\nawait fetch('/api/v1/v1/push.token', {\n  method: 'POST',\n  headers: { 'X-User-Id': uid, 'X-Auth-Token': rawToken, 'Content-Type': 'application/json' },\n  body: JSON.stringify({ id, type, value, appName }),\n});","typeGuard":"function hasAuthToken(headers: Record<string,string>): boolean {\n  return !!headers['x-auth-token'];\n}","tryCatchPattern":"null","preventionTips":["Always send both X-User-Id and X-Auth-Token on push endpoints.","Verify proxies/gateways forward x-auth-token unchanged.","Store the raw token in the same session key the SDK uses."],"tags":["push","authentication","header","token","mobile"],"backgroundTag":null,"analyzedSha":"f9d3ec372bb580fa8d036f94cf03925a478ef768","analyzedAt":"2026-08-12T19:07:17.372Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}