{"record":{"id":"de738b0128f97f92","repo":"calcom/cal.diy","slug":"start-date-must-be-before-end-date","errorCode":null,"errorMessage":"Start date must be before end date.","messagePattern":"Start date must be before end date\\.","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"apps/api/v2/src/modules/ooo/services/ooo.service.ts","lineNumber":51,"sourceCode":"  ) {}\n\n  formatOooReason(ooo: OutOfOfficeEntry) {\n    return {\n      ...ooo,\n      reason: ooo.reasonId\n        ? OOO_REASON_ID_TO_REASON[ooo.reasonId as keyof typeof OOO_REASON_ID_TO_REASON]\n        : OOO_REASON_ID_TO_REASON[1],\n    };\n  }\n\n  isStartBeforeEnd(start?: Date, end?: Date) {\n    if ((end && !start) || (start && !end)) {\n      throw new BadRequestException(\"Please specify both ooo start and end time.\");\n    }\n\n    if (start && end) {\n      if (start.getTime() > end.getTime()) {\n        throw new BadRequestException(\"Start date must be before end date.\");\n      }\n    }\n    return true;\n  }\n\n  async checkUserEligibleForRedirect(userId: number, toUserId?: number) {\n    if (toUserId) {\n      const user = await this.usersRepository.findUserOOORedirectEligible(userId, toUserId);\n      if (!user) {\n        throw new BadRequestException(\"Cannot redirect to this user.\");\n      }\n    }\n  }\n\n  async checkExistingOooRedirect(userId: number, start?: Date, end?: Date, toUserId?: number) {\n    if (start && end) {\n      const existingOooRedirect = await this.oooRepository.findExistingOooRedirect(\n        userId,","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/calcom/cal.diy/blob/176037d0afbe572f870a3c702985e7cd83fe6c0c/apps/api/v2/src/modules/ooo/services/ooo.service.ts#L33-L69","documentation":"Thrown by isStartBeforeEnd() when both start and end are present but start.getTime() > end.getTime(). The OOO window must be non-negative in duration; an inverted range is rejected. Equality (start === end) is permitted because the comparison is strictly greater-than.","triggerScenarios":"POST/PATCH /v2/ooo where the client orders the bounds backwards, computes end by subtracting instead of adding a duration, or swaps day/month in one of the dates so the parsed instant inverts.","commonSituations":"Date pickers returning end before start when the user clears and re-picks; timezone math that shifts start past end across a DST boundary; copy-paste fixtures with reversed dates; month/day transposition (MM-DD vs DD-MM) on one field.","solutions":["Client-side: assert start <= end before submitting, and swap the values if the user picked them in reverse.","Compute end from start plus a positive delta (e.g. start + N days) rather than constructing both independently.","Render a disabled submit button until start <= end in the UI to fail fast."],"exampleFix":"// before\nconst end = new Date(start.getTime() - days * 86400000); // inverted\n// after\nconst end = new Date(start.getTime() + days * 86400000);\n// guard before submit\nif (start.getTime() > end.getTime()) throw new Error('start must precede end');","handlingStrategy":"validation","validationCode":"function assertOrder(start: Date, end: Date) {\n  if (start.getTime() > end.getTime())\n    throw new Error('start must be <= end');\n  return { start: start.toISOString(), end: end.toISOString() };\n}","typeGuard":"const isValidRange = (start?: Date, end?: Date): boolean =>\n  !start || !end ? true : start.getTime() <= end.getTime();","tryCatchPattern":null,"preventionTips":["Compute end as start + positive delta rather than constructing independently.","Disable submit when start > end in the picker.","Watch for month/day transposition and DST shifts that can invert a window."],"tags":["business-rule","ooo","date-validation","nestjs"],"backgroundTag":null,"analyzedSha":"176037d0afbe572f870a3c702985e7cd83fe6c0c","analyzedAt":"2026-08-12T19:12:41.464Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}