RocketChat/Rocket.Chat · error · Meteor.Error
error-invite-expired
error-invite-expired
Error message
The invite token has expired.
What it means
Thrown by validateInviteToken when inviteData.expires is set and its timestamp is <= Date.now() (validateInviteToken.ts:38). This is the time-based half of invite expiry; details { field: 'expires' } distinguish it from the usage-limit case. The comparison is wall-clock, so server clock skew matters.
Source
Thrown at apps/meteor/server/lib/rooms/invites/validateInviteToken.ts:38
}
const room = await Rooms.findOneById(inviteData.rid);
if (!room) {
throw new Meteor.Error('error-invalid-room', 'The invite token is invalid.', {
method: 'validateInviteToken',
field: 'rid',
});
}
if (settings.get('ABAC_Enabled') && room?.abacAttributes?.length) {
throw new Meteor.Error('error-invalid-room', 'Room is ABAC managed', {
method: 'validateInviteToken',
field: 'rid',
});
}
if (inviteData.expires && new Date(inviteData.expires).getTime() <= Date.now()) {
throw new Meteor.Error('error-invite-expired', 'The invite token has expired.', {
method: 'validateInviteToken',
field: 'expires',
});
}
if (inviteData.maxUses > 0 && inviteData.uses >= inviteData.maxUses) {
throw new Meteor.Error('error-invite-expired', 'The invite token has expired.', {
method: 'validateInviteToken',
field: 'maxUses',
});
}
return {
inviteData,
room,
};
};
View on GitHub (pinned to b2c16d5842)
Solutions
- Have a room admin generate a new invite (or one with a longer/no expiration)
- Verify server time (date, NTP status) if seemingly fresh invites are rejected
- Pre-check server-side: if inviteData.expires && new Date(inviteData.expires).getTime() <= Date.now(), skip the call and surface a friendly 'link expired' message
- Use details.field === 'expires' to differentiate from 'maxUses' exhaustion
Example fix
// before
const { inviteData, room } = await validateInviteToken(token);
// after
const inviteData = await Invites.findOneById(token);
if (inviteData?.expires && new Date(inviteData.expires).getTime() <= Date.now()) {
throw new Meteor.Error('error-invite-expired', 'This invite link has expired. Ask for a new one.');
}
const { inviteData: valid, room } = await validateInviteToken(token); Defensive patterns
Strategy: validation
Validate before calling
const invite = await Invites.findOneById(token);
if (invite?.expires && new Date(invite.expires).getTime() <= Date.now()) {
throw new Meteor.Error('error-invite-expired', 'This invite link has expired — ask for a new one');
}
const result = await validateInviteToken(token); Try / catch
try {
await validateInviteToken(token);
} catch (e) {
if (e instanceof Meteor.Error && e.error === 'error-invite-expired' && e.details?.field === 'expires') {
// time-based expiry: prompt for a fresh invite
}
} Prevention
- Keep server clocks NTP-synced so fresh invites are not falsely expired
- Default new invites to sensible expirations; avoid very short windows for widely shared links
- Branch on details.field ('expires' vs 'maxUses') instead of the shared message
When it happens
Trigger: An invite created with a finite expiration (days setting) whose deadline has passed; server clock drifted forward making fresh invites appear expired; long-lived invite links in documentation that outlived their expires value.
Common situations: Admin created a 1-day invite and the user clicks it on day 2; NTP misconfiguration on the server; invites generated during testing kept being reused after their window closed.
Related errors
- error-invalid-token
- Invalid command parameter provided, must be a string.
- Command does not exist in the system currently: "${cmd}"
- error-empty-invite-list
- error-invalid-user
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/303991a4b8371638.
Report an issue: GitHub.