{"record":{"id":"64e0f6b2ca67ddbc","repo":"RocketChat/Rocket.Chat","slug":"invalid-token-64e0f6","errorCode":"invalid-token","errorMessage":"invalid-token","messagePattern":"invalid-token","errorType":"exception","errorClass":"Meteor.Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/api/v1/omnichannel/visitor.ts","lineNumber":112,"sourceCode":"\t\t\tif (!result) {\n\t\t\t\treturn API.v1.success({ visitor });\n\t\t\t}\n\n\t\t\treturn API.v1.success({ visitor: await VisitorsRaw.findOneEnabledById(visitor._id) });\n\t\t},\n\t},\n);\n\nAPI.v1.addRoute('livechat/visitor/:token', {\n\tasync get() {\n\t\tcheck(this.urlParams, {\n\t\t\ttoken: String,\n\t\t});\n\n\t\tconst visitor = await VisitorsRaw.getVisitorByToken(this.urlParams.token, {});\n\n\t\tif (!visitor) {\n\t\t\tthrow new Meteor.Error('invalid-token');\n\t\t}\n\n\t\treturn API.v1.success({ visitor });\n\t},\n\tasync delete() {\n\t\tcheck(this.urlParams, {\n\t\t\ttoken: String,\n\t\t});\n\n\t\tconst visitor = await VisitorsRaw.getVisitorByToken(this.urlParams.token, {});\n\t\tif (!visitor) {\n\t\t\tthrow new Meteor.Error('invalid-token');\n\t\t}\n\t\tconst extraQuery = await callbacks.run('livechat.applyRoomRestrictions', {}, { userId: this.userId });\n\t\tconst rooms = await LivechatRooms.findOpenByVisitorToken(\n\t\t\tthis.urlParams.token,\n\t\t\t{\n\t\t\t\tprojection: {","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/api/v1/omnichannel/visitor.ts#L94-L130","documentation":"Thrown by GET /api/v1/livechat/visitor/:token (unauthenticated, used by the widget) when no visitor document matches the given token. The token identifies the visitor across sessions; if it was never registered (no prior POST /api/v1/livechat/visitor) or was deleted, the lookup returns nothing and the Meteor error 'invalid-token' is returned.","triggerScenarios":"Calling GET with a random/typo'd token; querying before registration completed; the visitor was deleted by DELETE /api/v1/livechat/visitor/:token (or GDPR cleanup) and the widget still holds the old token in localStorage.","commonSituations":"Widget state wiped or users switching browsers/devices where the stored token never existed on the server; test scripts that generate a token but never POST it; stale tokens after workspace data pruning.","solutions":["Register first: POST /api/v1/livechat/visitor with the token, then GET /api/v1/livechat/visitor/<token>","On 400 invalid-token, regenerate a fresh token, register, and continue — treat it as 'unknown visitor'","Make sure the token is persisted (localStorage) and identical (case, whitespace) on both calls"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const res = await fetch(`${server}/api/v1/livechat/visitor/${encodeURIComponent(token)}`);\nif (res.status === 400) { // invalid-token: unknown token\n  await registerVisitor(token);   // POST /api/v1/livechat/visitor\n  return fetch(`${server}/api/v1/livechat/visitor/${encodeURIComponent(token)}`);\n}","typeGuard":null,"tryCatchPattern":"const body = await (await fetch(url)).json();\nif (!body.success && body.error === 'invalid-token') { /* unknown visitor: register then retry, or treat as new session */ }","preventionTips":["Always POST (register) the token before GETting it","Persist the token so return visitors keep their identity","Treat invalid-token on GET as a benign 'not found', not a fatal error"],"tags":["livechat-widget","visitor","token","rest-api","not-found"],"backgroundTag":"resource-not-found","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"}