RocketChat/Rocket.Chat · error · Error
invalid ISO 8601 date
Error message
invalid ISO 8601 date
What it means
mapDateForAPI validates engagement-dashboard date parameters by round-trip: it parses the string with Date.parse and requires new Date(timestamp).toISOString() to equal the input exactly. Only the canonical UTC form with milliseconds and a Z suffix (e.g. 2024-01-01T00:00:00.000Z) passes. Date-only strings, offsets like +02:00, missing milliseconds, or trailing characters all throw 'invalid ISO 8601 date'.
Solutions
- Send dates produced by new Date(...).toISOString(), which always yields UTC with .SSSZ
- If using moment: moment(date).toISOString()
- Validate input with the exported isDateISOString helper (or an equivalent round-trip check) before calling mapDateForAPI
Example fix
// before
mapDateForAPI('2024-01-01T00:00:00+00:00'); // throws
// after
mapDateForAPI(new Date('2024-01-01T00:00:00+00:00').toISOString()); // '2024-01-01T00:00:00.000Z' Defensive patterns
Strategy: type-guard
Validate before calling
const start = new Date(Date.now() - 86400000).toISOString();
const end = new Date().toISOString();
if (!isISODateString(start) || !isISODateString(end)) {
throw new TypeError('dates must be canonical ISO 8601 UTC strings');
}
const range = { start: mapDateForAPI(start), end: mapDateForAPI(end) }; Type guard
const isISODateString = (input: string): input is string => {
const timestamp = Date.parse(input);
return !Number.isNaN(timestamp) && new Date(timestamp).toISOString() === input;
}; Try / catch
try {
const from = mapDateForAPI(raw);
} catch (err) {
if (err instanceof Error && err.message === 'invalid ISO 8601 date') {
// reject the request with a 400 and a hint to use toISOString()
} else {
throw err;
}
} Prevention
- Always build API date params with new Date(...).toISOString(), never manual string formatting
- Remember the round-trip check requires milliseconds and a Z suffix (UTC)
- Centralize date serialization in one helper for all dashboard calls
When it happens
Trigger: Calling an engagement dashboard endpoint (apps/meteor ee engagement dashboard handlers that use mapDateForAPI) with start/end params such as '2024-01-01' or '2024-01-01T00:00:00+00:00' instead of '2024-01-01T00:00:00.000Z'.
Common situations: Hand-built query strings; serializing dates with moment().format('YYYY-MM-DDTHH:mm:ss') without millisecond/Z precision; passing timestamps from systems that emit offset-based ISO strings.
Related errors
- error-invalid-param
- error-roomId-param-invalid
- duplicated-account
- error-bio-size-exceeded
- error-bio-size-exceeded
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/7d8e322ee9149ac9.
Report an issue: GitHub.
Appendix: source
Thrown at apps/meteor/ee/server/lib/engagementDashboard/date.ts:14
import mem from 'mem';
import moment from 'moment';
export const isDateISOString = mem(
(input: string): input is string => {
const timestamp = Date.parse(input);
return !Number.isNaN(timestamp) && new Date(timestamp).toISOString() === input;
},
{ maxAge: 10000 },
);
export const mapDateForAPI = (input: string): Date => {
if (!isDateISOString(input)) {
throw new Error('invalid ISO 8601 date');
}
return new Date(Date.parse(input));
};
export const convertDateToInt = (date: Date): number => parseInt(moment(date).clone().format('YYYYMMDD'), 10);
export const convertIntToDate = (intValue: number): Date => moment(intValue, 'YYYYMMDD').clone().toDate();
const diffBetweenDays = (start: string | number | Date, end: string | number | Date): number =>
moment(new Date(start)).clone().diff(new Date(end), 'days');
export const diffBetweenDaysInclusive = (start: string | number | Date, end: string | number | Date): number =>
diffBetweenDays(start, end) + 1;
export const getTotalOfWeekItems = <T extends Record<string, number>>(weekItems: T[], property: keyof T): number =>
weekItems.reduce((acc, item) => {
acc += item[property];
return acc;
}, 0);
View on GitHub (pinned to b2c16d5842)