calcom/cal.diy · error · BadRequestException

Start date must be before end date.

Error message

Start date must be before end date.

What it means

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.

Source

Thrown at apps/api/v2/src/modules/ooo/services/ooo.service.ts:51

  ) {}

  formatOooReason(ooo: OutOfOfficeEntry) {
    return {
      ...ooo,
      reason: ooo.reasonId
        ? OOO_REASON_ID_TO_REASON[ooo.reasonId as keyof typeof OOO_REASON_ID_TO_REASON]
        : OOO_REASON_ID_TO_REASON[1],
    };
  }

  isStartBeforeEnd(start?: Date, end?: Date) {
    if ((end && !start) || (start && !end)) {
      throw new BadRequestException("Please specify both ooo start and end time.");
    }

    if (start && end) {
      if (start.getTime() > end.getTime()) {
        throw new BadRequestException("Start date must be before end date.");
      }
    }
    return true;
  }

  async checkUserEligibleForRedirect(userId: number, toUserId?: number) {
    if (toUserId) {
      const user = await this.usersRepository.findUserOOORedirectEligible(userId, toUserId);
      if (!user) {
        throw new BadRequestException("Cannot redirect to this user.");
      }
    }
  }

  async checkExistingOooRedirect(userId: number, start?: Date, end?: Date, toUserId?: number) {
    if (start && end) {
      const existingOooRedirect = await this.oooRepository.findExistingOooRedirect(
        userId,

View on GitHub (pinned to 176037d0af)

Solutions

  1. Client-side: assert start <= end before submitting, and swap the values if the user picked them in reverse.
  2. Compute end from start plus a positive delta (e.g. start + N days) rather than constructing both independently.
  3. Render a disabled submit button until start <= end in the UI to fail fast.

Example fix

// before
const end = new Date(start.getTime() - days * 86400000); // inverted
// after
const end = new Date(start.getTime() + days * 86400000);
// guard before submit
if (start.getTime() > end.getTime()) throw new Error('start must precede end');
Defensive patterns

Strategy: validation

Validate before calling

function assertOrder(start: Date, end: Date) {
  if (start.getTime() > end.getTime())
    throw new Error('start must be <= end');
  return { start: start.toISOString(), end: end.toISOString() };
}

Type guard

const isValidRange = (start?: Date, end?: Date): boolean =>
  !start || !end ? true : start.getTime() <= end.getTime();

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/de738b0128f97f92. Report an issue: GitHub.