RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-date

error-invalid-date

Error message

Invalid expiresAt date string

What it means

Thrown by POST /api/v1/users.setStatus when expiresAt is supplied but is not parseable as a date — new Date(expiresAt) yields NaN. It uses code error-invalid-date with method 'users.setStatus' and only checks this format problem; range (future) is a separate error.

Solutions

  1. Send an ISO-8601 string: new Date(x).toISOString()
  2. Validate with !Number.isNaN(Date.parse(expiresAt)) before calling
  3. Pass milliseconds number instead of an ambiguous string if your client supports it
  4. Strip/trim user-supplied date inputs

Example fix

// before
await sdk.post('users.setStatus', { status, expiresAt: '31/12/2026' });
// after
await sdk.post('users.setStatus', { status, expiresAt: new Date('2026-12-31T00:00:00Z').toISOString() });
Defensive patterns

Strategy: validation

Validate before calling

if (expiresAt != null && Number.isNaN(Date.parse(expiresAt))) throw new TypeError(`invalid expiresAt: ${expiresAt}`);
await sdk.post('users.setStatus', { status, message, expiresAt: expiresAt ? new Date(expiresAt).toISOString() : undefined });

Type guard

const isValidIsoDate = (v: unknown): v is string => typeof v === 'string' && !Number.isNaN(Date.parse(v));

Try / catch

try { await sdk.post('users.setStatus', body); } catch (e) { if (e.response?.data?.errorType === 'error-invalid-date' && /Invalid expiresAt/.test(e.response.data.message)) { body.expiresAt = new Date().toISOString(); await sdk.post('users.setStatus', body); } else throw e; }

Prevention

When it happens

Trigger: POST users.setStatus {"status":"away","expiresAt":"tomorrow"}; numeric epoch seconds sent instead of an ISO-8601 string where the runtime parses it as invalid; localised date formats like 31/12/2026; trailing whitespace or BOM in the value.

Common situations: Client code formatting dates with locale-specific toString(); passing epoch seconds vs milliseconds confusion; strings from a form input never normalized.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@e4b8178b20 (2026-08-18). Data as JSON: /api/errors/9c3b1fb2d5e85510. Report an issue: GitHub.

Appendix: source

Thrown at apps/meteor/server/api/v1/users.ts:2059

					return getUserFromParams(this.bodyParams);
				}
			})();

			if (!user) {
				return API.v1.forbidden();
			}

			const { status, message, expiresAt } = this.bodyParams;

			if (message && !settings.get('Accounts_AllowUserStatusMessageChange')) {
				throw new Meteor.Error('error-not-allowed', 'Change status is not allowed', {
					method: 'users.setStatus',
				});
			}

			const statusExpiresAt = expiresAt ? new Date(expiresAt) : undefined;
			if (statusExpiresAt && Number.isNaN(statusExpiresAt.getTime())) {
				throw new Meteor.Error('error-invalid-date', 'Invalid expiresAt date string', {
					method: 'users.setStatus',
				});
			}

			if (statusExpiresAt && statusExpiresAt.getTime() <= Date.now()) {
				throw new Meteor.Error('error-invalid-date', 'expiresAt must be a future date', {
					method: 'users.setStatus',
				});
			}

			// If status is missing (message-only update), keep the user's chosen status (statusDefault),
			// not the computed status — otherwise a transient auto-away/offline gets pinned as a manual claim.
			const effectiveStatus = status || user.statusDefault || ('online' as UserStatus);

			if (effectiveStatus === 'offline' && !settings.get('Accounts_AllowInvisibleStatusOption')) {
				throw new Meteor.Error('error-status-not-allowed', 'Invisible status is disabled', {
					method: 'users.setStatus',
				});

View on GitHub (pinned to e4b8178b20)