{"record":{"id":"2c8337b54b22a69f","repo":"RocketChat/Rocket.Chat","slug":"invalid-command-parameter-provided-must-be-a-stri","errorCode":null,"errorMessage":"Invalid command parameter provided, must be a string.","messagePattern":"Invalid command parameter provided, must be a string\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/meteor/app/apps/server/bridges/commands.ts","lineNumber":35,"sourceCode":"\t}\n\n\tprotected async doesCommandExist(command: string, appId: string): Promise<boolean> {\n\t\tthis.orch.debugLog(`The App ${appId} is checking if \"${command}\" command exists.`);\n\n\t\tif (typeof command !== 'string' || command.length === 0) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst cmd = command.toLowerCase();\n\n\t\treturn typeof slashCommands.commands[cmd] === 'object' || this.disabledCommands.has(cmd);\n\t}\n\n\tprotected async enableCommand(command: string, appId: string): Promise<void> {\n\t\tthis.orch.debugLog(`The App ${appId} is attempting to enable the command: \"${command}\"`);\n\n\t\tif (typeof command !== 'string' || command.trim().length === 0) {\n\t\t\tthrow new Error('Invalid command parameter provided, must be a string.');\n\t\t}\n\n\t\tconst cmd = command.toLowerCase();\n\t\tif (!this.disabledCommands.has(cmd)) {\n\t\t\tthrow new Error(`The command is not currently disabled: \"${cmd}\"`);\n\t\t}\n\n\t\tslashCommands.commands[cmd] = this.disabledCommands.get(cmd) as (typeof slashCommands.commands)[string];\n\t\tthis.disabledCommands.delete(cmd);\n\n\t\tvoid this.orch.getNotifier().commandUpdated(cmd);\n\t}\n\n\tprotected async disableCommand(command: string, appId: string): Promise<void> {\n\t\tthis.orch.debugLog(`The App ${appId} is attempting to disable the command: \"${command}\"`);\n\n\t\tif (typeof command !== 'string' || command.trim().length === 0) {\n\t\t\tthrow new Error('Invalid command parameter provided, must be a string.');","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/app/apps/server/bridges/commands.ts#L17-L53","documentation":"The authorize step hashes the submitted token with Accounts._hashLoginToken and searches users by services.resume.loginTokens.hashedToken. If no user matches, it responds 401 'Invalid token': the token was forged, truncated, revoked, or expired. Logging out deletes a user's resume tokens, so a token captured before logout becomes invalid immediately.","triggerScenarios":"POST /oauth/authorize with allow=yes and a token that matches no services.resume.loginTokens entry: token from a user who has since logged out; token truncated or mangled in transit; token issued by a different deployment/database (dev vs prod); forged values.","commonSituations":"Consent submitted after the user logged out elsewhere; DB restored or reset so old tokens no longer exist; multi-replica setups where sessions are not shared; copy-paste errors in manual API testing.","solutions":["Log in again and resubmit the consent with the fresh resume token","Make sure the token comes from the same environment/DB the OAuth server validates against","Send the complete resume token string (no quotes/whitespace) as access_token or token","If it persists, verify the user document still contains services.resume.loginTokens entries"],"exampleFix":"// before: stale token from an earlier session\nbody: new URLSearchParams({ allow: 'yes', access_token: oldToken });\n// after: fetch a fresh resume token first\nconst { data } = await (await fetch('/api/v1/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ user: 'me', password: '***' }) })).json();\nbody: new URLSearchParams({ allow: 'yes', access_token: data.authToken });","handlingStrategy":"validation","validationCode":"const me = await fetch('/api/v1/me', { headers: { 'X-Auth-Token': token, 'X-User-Id': uid } });\nif (!me.ok) { /* token invalid/expired: re-login before calling /oauth/authorize */ }","typeGuard":null,"tryCatchPattern":"On 401 'Invalid token', discard the stored resume token, re-authenticate, then retry the authorize POST once with the fresh token — more than one retry indicates a deeper session problem.","preventionTips":["Validate resume tokens against /api/v1/me before using them in the consent flow","Never reuse tokens across environments or after logout","Send the token verbatim (no trimming/encoding beyond URL form rules)"],"tags":["oauth2","meteor","login-token","token-expired","http-401"],"backgroundTag":"invalid-or-expired-auth-token","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"}