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
- Pass an ISO 8601 UTC string: updatedSince=2024-06-01T00:00:00.000Z.
- If you meant 'everything', first call chat.getThreadsList once, store the max _updatedAt, then use that timestamp for subsequent syncs.
- 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
- Always serialize dates with toISOString().
- Remember sync endpoints require updatedSince — the plain get variants do not accept it.
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
- error-creating-custom-user-status
- error-e2e-key-reset-in-progress
- error-invalid-message
- error-invalid-param
- error-invalid-query
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)