RocketChat/Rocket.Chat · error · Meteor.Error

error-updatedSince-param-invalid

error-updatedSince-param-invalid

Error message

The "updatedSince" query parameter must be a valid date.

What it means

Thrown by GET chat.syncThreadsList when the updatedSince query parameter cannot be parsed as a date: the endpoint runs isNaN(Date.parse(updatedSince)) and throws Meteor.Error('error-updatedSince-param-invalid'). Because updatedSince is destructured from queryParams, omitting it yields undefined, Date.parse(undefined) is NaN, so the call fails both for missing and malformed values — updatedSince is effectively required on sync endpoints.

Solutions

  1. Pass an ISO 8601 UTC string: updatedSince=2024-06-01T00:00:00.000Z.
  2. If you meant 'everything', first call chat.getThreadsList once, store the max _updatedAt, then use that timestamp for subsequent syncs.
  3. Encode the date client-side with new Date().toISOString() to avoid locale formatting issues.

Example fix

// before
fetch('/api/v1/chat.syncThreadsList?rid=RID&updatedSince=' + Date.now());

// after
fetch('/api/v1/chat.syncThreadsList?rid=RID&updatedSince=' + new Date(lastSync).toISOString());
Defensive patterns

Strategy: validation

Validate before calling

const since = params.updatedSince;
if (since === undefined || Number.isNaN(Date.parse(since))) {
  throw new Error('updatedSince must be an ISO 8601 date string');
}
// Rocket.Chat expects ISO 8601; reject epoch numbers and locale formats
if (typeof since === 'number') throw new Error('pass new Date(ms).toISOString(), not epoch');

Type guard

function isIsoDateString(v: unknown): v is string {
  return typeof v === 'string' && !Number.isNaN(Date.parse(v)) && /Z$|[+-]\d{2}:?\d{2}$/.test(v.trim());
}

Try / catch

try { await api.get('chat.syncThreadsList', { rid, updatedSince }); }
catch (e) {
  if (e.error === 'error-updatedSince-param-invalid') { since = new Date().toISOString(); retry(); } else throw e;
}

Prevention

When it happens

Trigger: Calling chat.syncThreadsList without updatedSince, with an empty value, or with a non-ISO string such as '01-02-2024' variants the JS parser rejects, e.g. 'yesterday', '2024/13/45', or a raw Unix timestamp like '1700000000'.

Common situations: Porting code from the non-sync chat.getThreadsList (where updatedSince does not exist) and forgetting the param; passing epoch seconds instead of an ISO string; passing a date formatted by a locale-specific formatter.

Understand the failure class

Background: "Invalid query parameter" / "Failed to parse value of ...": fixing bad query string parameters across APIs — this error's family across 36 libraries.

Related errors


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

Appendix: source

Thrown at apps/meteor/server/api/v1/chat.ts:1162

					},
					required: ['threads', 'success'],
					additionalProperties: false,
				}),
				400: validateBadRequestErrorResponse,
				401: validateUnauthorizedErrorResponse,
			},
		},
		async function action() {
			const { rid } = this.queryParams;
			const { query, fields, sort } = await this.parseJsonQuery();
			const { updatedSince } = this.queryParams;
			let updatedSinceDate;
			if (!settings.get<boolean>('Threads_enabled')) {
				throw new Meteor.Error('error-not-allowed', 'Threads Disabled');
			}

			if (isNaN(Date.parse(updatedSince))) {
				throw new Meteor.Error('error-updatedSince-param-invalid', 'The "updatedSince" query parameter must be a valid date.');
			} else {
				updatedSinceDate = new Date(updatedSince);
			}
			const user = await Users.findOneById(this.userId, { projection: { _id: 1 } });
			const room = await Rooms.findOneById(rid, { projection: { ...roomAccessAttributes, t: 1, _id: 1 } });

			if (!room || !user || !(await canAccessRoomAsync(room, user))) {
				throw new Meteor.Error('error-not-allowed', 'Not Allowed');
			}
			const threadQuery = Object.assign({}, query, { rid, tcount: { $exists: true } });
			return API.v1.success({
				threads: {
					update: await Messages.find(
						{ ...threadQuery, _updatedAt: { $gt: updatedSinceDate } },
						{
							sort,
							projection: fields,
						},

View on GitHub (pinned to e4b8178b20)