RocketChat/Rocket.Chat · error · Meteor.Error
error-invalid-user-id
error-invalid-user-id
Error message
Invalid user id
What it means
Thrown by POST /api/v1/users.removeOtherSessions when Users.removeNonPATLoginTokensExcept(userId, hashedToken) returns falsy — i.e. the supplied x-auth-token does not hash-match any persisted non-PAT login token for the user, so the 'keep this session, revoke the rest' operation cannot proceed. Reported as error-invalid-user-id / 'Invalid user id' even though the real problem is the token.
Solutions
- Use the authToken from a real login response, not a PAT, for this endpoint
- Re-login to obtain a fresh token and retry
- Verify the token belongs to x-user-id by calling a cheap authenticated endpoint first
- If a PAT-only flow is required, use users.logout with logoutOtherSessions semantics or create a proper session
Example fix
// before
await sdk.post('users.removeOtherSessions', {}, { 'x-auth-token': pat }); // PAT -> always fails
// after
const { data } = await sdk.post('login', { user, password });
await sdk.post('users.removeOtherSessions', {}, { 'x-auth-token': data.authToken }); Defensive patterns
Strategy: validation
Validate before calling
const { data } = await sdk.post('login', { user, password });
if (!data.authToken) throw new Error('login did not yield a resume token');
await sdk.post('users.removeOtherSessions', {}, { 'x-auth-token': data.authToken }); Type guard
async function tokenIsLive(token: string, uid: string): Promise<boolean> {
const res = await fetch(`${rc}/api/v1/me`, { headers: { 'x-auth-token': token, 'x-user-id': uid } });
return res.ok;
} Try / catch
try { await sdk.post('users.removeOtherSessions', {}, h); } catch (e) { if (e.response?.data?.errorType === 'error-invalid-user-id') { await relogin(); await sdk.post('users.removeOtherSessions', {}, freshHeaders); } else throw e; } Prevention
- Don't reuse PATs for session endpoints
- Refresh tokens after password changes
- Pair token with its owning userId
When it happens
Trigger: Passing a personal access token (PAT) value in x-auth-token (PATs live outside services.resume.loginTokens); passing an already-revoked or expired session token; passing a token belonging to another user.
Common situations: Automation scripts reusing a PAT for a session-scoped endpoint; stale tokens kept in local storage after a password change (which revokes tokens); mixed-up token/userId pairs in multi-account tooling.
Related errors
- error-parameter-required
- agent-not-found
- AI search request failed
- AI search status unavailable
- Cannot send system messages using 'chat.sendMessage'
AI-assisted analysis of RocketChat/Rocket.Chat@e4b8178b20 (2026-08-18).
Data as JSON: /api/errors/79b5f1e8ed02fd52.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/server/api/v1/users.ts:1681
tokenExpires: { type: 'string' },
success: { type: 'boolean', enum: [true] },
},
required: ['token', 'tokenExpires', 'success'],
additionalProperties: false,
}),
401: validateUnauthorizedErrorResponse,
},
},
async function action() {
const xAuthToken = this.request.headers.get('x-auth-token') as string;
if (!xAuthToken) {
throw new Meteor.Error('error-parameter-required', 'x-auth-token is required');
}
const hashedToken = Accounts._hashLoginToken(xAuthToken);
if (!(await Users.removeNonPATLoginTokensExcept(this.userId, hashedToken))) {
throw new Meteor.Error('error-invalid-user-id', 'Invalid user id');
}
const me = (await Users.findOneById(this.userId, { projection: { 'services.resume.loginTokens': 1 } })) as Pick<IUser, 'services'>;
void notifyOnUserChange({
clientAction: 'updated',
id: this.userId,
diff: { 'services.resume.loginTokens': me.services?.resume?.loginTokens },
});
const token = me.services?.resume?.loginTokens?.find((token) => token.hashedToken === hashedToken);
const loginExp = settings.get<number>('Accounts_LoginExpiration');
const tokenExpires = (token && 'when' in token && new Date(token.when.getTime() + getLoginExpirationInMs(loginExp))) || undefined;
return API.v1.success({
token: xAuthToken,View on GitHub (pinned to e4b8178b20)