{"record":{"id":"24d6f76a37fb9287","repo":"RocketChat/Rocket.Chat","slug":"error-roomid-param-invalid-24d6f7","errorCode":"error-roomId-param-invalid","errorMessage":"The \"updatedSince\" query parameter must be a valid date.","messagePattern":"The \"updatedSince\" query parameter must be a valid date\\.","errorType":"exception","errorClass":"Meteor.Error","httpStatus":400,"severity":"error","filePath":"apps/meteor/server/api/v1/permissions.ts","lineNumber":125,"sourceCode":"\t\t\t\t\t\t\ttype: 'boolean',\n\t\t\t\t\t\t\tenum: [true],\n\t\t\t\t\t\t\tdescription: 'Indicates if the request was successful.',\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t\trequired: ['update', 'remove', 'success'],\n\t\t\t\t\tadditionalProperties: false,\n\t\t\t\t}),\n\t\t\t\t400: validateBadRequestErrorResponse,\n\t\t\t\t401: validateUnauthorizedErrorResponse,\n\t\t\t},\n\t\t},\n\t\tasync function action() {\n\t\t\tconst { updatedSince } = this.queryParams;\n\n\t\t\tlet updatedSinceDate: Date | undefined;\n\t\t\tif (updatedSince) {\n\t\t\t\tif (isNaN(Date.parse(updatedSince))) {\n\t\t\t\t\tthrow new Meteor.Error('error-roomId-param-invalid', 'The \"updatedSince\" query parameter must be a valid date.');\n\t\t\t\t}\n\t\t\t\tupdatedSinceDate = new Date(updatedSince);\n\t\t\t}\n\n\t\t\tconst result = (await permissionsGetMethod(updatedSinceDate)) as {\n\t\t\t\tupdate: IPermission[];\n\t\t\t\tremove: IPermission[];\n\t\t\t};\n\n\t\t\tif (Array.isArray(result)) {\n\t\t\t\treturn API.v1.success({\n\t\t\t\t\tupdate: result,\n\t\t\t\t\tremove: [],\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn API.v1.success(result);\n\t\t},","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/server/api/v1/permissions.ts#L107-L143","documentation":"Thrown by GET /api/v1/permissions.listAll when the optional `updatedSince` query param is present but Date.parse cannot interpret it. Note the error code is misleading legacy reuse: 'error-roomId-param-invalid' has nothing to do with rooms here — the actual problem is a malformed date string such as '2024-13-01', 'yesterday', or a double-encoded ISO timestamp.","triggerScenarios":"GET /api/v1/permissions.listAll?updatedSince=<bad> with values like '01/02/2024' being sent without timezone care, non-ISO strings ('now', 'last-week'), or an ISO string mangled by URL encoding (spaces/%2B becoming '+' and being stripped). Omitting updatedSince entirely is fine — the error only fires when the param exists and Date.parse returns NaN.","commonSituations":"Client builds the date with locale-dependent formatting instead of toISOString(); a '+' in the timezone offset gets decoded to a space by the query parser, making the string unparseable; synced clients that last-run timestamps as raw epoch numbers or human strings.","solutions":["Always send an ISO-8601 string produced by new Date().toISOString() (e.g. 2024-05-01T12:00:00.000Z)","If you store last-sync time, persist the ISO string verbatim rather than reformatting it","URL-encode the value so timezone pluses survive transport (encodeURIComponent)"],"exampleFix":"// before\nconst url = `permissions.listAll?updatedSince=${lastSync}`; // 'last sync'\n\n// after\nconst url = `permissions.listAll?updatedSince=${encodeURIComponent(new Date(lastSyncMs).toISOString())}`;","handlingStrategy":"validation","validationCode":"const updatedSince = new Date(lastSyncMs).toISOString();\nif (Number.isNaN(Date.parse(updatedSince))) throw new Error('bad updatedSince cursor');\nawait sdk.get('permissions.listAll', { updatedSince: encodeURIComponent(updatedSince) });","typeGuard":"const isValidDateString = (v: unknown): v is string =>\n  typeof v === 'string' && v.length > 0 && !Number.isNaN(Date.parse(v));","tryCatchPattern":"catch the 400 and inspect body.error — despite the misleading 'error-roomId-param-invalid' code, only updatedSince is at fault; rebuild it with toISOString() and retry once.","preventionTips":["Always generate timestamps with toISOString()","Encode query values with encodeURIComponent so '+' timezones survive","Store sync cursors as ISO strings or epoch millis, never preformatted local strings"],"tags":["permissions","validation","date","query-params","rest-api"],"backgroundTag":"invalid-date-format","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}