calcom/cal.diy · warning · BadRequestException

'to' must not be before 'from'

Error message

'to' must not be before 'from'

What it means

A class-validator custom constraint (IsAfterFrom) on FreebusyUnifiedInput throws a BadRequestException when the `to` field, parsed as a Date, is earlier than the `from` field. The validator deliberately throws rather than returning false, so the message reads exactly as written. The fields are validated as ISO8601 strings by @IsISO8601 first.

Source

Thrown at apps/api/v2/src/modules/cal-unified-calendars/inputs/freebusy-unified.input.ts:19

import { BadRequestException } from "@nestjs/common";
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import {
  IsISO8601,
  IsOptional,
  IsTimeZone,
  Validate,
  ValidationArguments,
  ValidatorConstraint,
  ValidatorConstraintInterface,
} from "class-validator";

@ValidatorConstraint({ name: "isAfterFrom", async: false })
class IsAfterFrom implements ValidatorConstraintInterface {
  validate(to: string, args: ValidationArguments) {
    const obj = args.object as { from?: string };
    if (!obj.from || !to) return true;
    if (new Date(to).getTime() < new Date(obj.from).getTime()) {
      throw new BadRequestException("'to' must not be before 'from'");
    }
    return true;
  }
  defaultMessage() {
    return "'to' must not be before 'from'";
  }
}

export class FreebusyUnifiedInput {
  @IsISO8601()
  @ApiProperty({
    type: String,
    description: "Start of the date range (ISO 8601 date or date-time)",
    example: "2026-03-10",
  })
  from!: string;

  @IsISO8601()

View on GitHub (pinned to 176037d0af)

Solutions

  1. Ensure the client sends `from` <= `to` (preferably from < to).
  2. Validate the range client-side before the request: `if (new Date(to) < new Date(from)) throw ...`.
  3. When converting local times to ISO, convert both ends through the same timezone so they stay ordered.

Example fix

// before
const body = { from: '2026-03-10T23:00:00Z', to: '2026-03-10T01:00:00Z' };

// after
const body = { from: '2026-03-10T01:00:00Z', to: '2026-03-10T23:00:00Z' };
Defensive patterns

Strategy: validation

Validate before calling

function isValidRange(from: string, to: string): boolean {
  const f = new Date(from).getTime();
  const t = new Date(to).getTime();
  return !Number.isNaN(f) && !Number.isNaN(t) && t >= f;
}
if (!isValidRange(body.from, body.to)) throw new Error("'to' must not be before 'from'");

Type guard

function isISO8601Pair(v: unknown): v is { from: string; to: string } {
  if (typeof v !== 'object' || v === null) return false;
  const o = v as any;
  return typeof o.from === 'string' && typeof o.to === 'string'
    && !Number.isNaN(new Date(o.from).getTime())
    && !Number.isNaN(new Date(o.to).getTime());
}

Prevention

When it happens

Trigger: POST /v2/.../free-busy (or any endpoint accepting FreebusyUnifiedInput) with body { from: '2026-03-10', to: '2026-03-09' }, or any payload where new Date(to).getTime() < new Date(from).getTime(). Also triggered by timezone-shifted date-times that make `to` fall before `from` in UTC.

Common situations: Client swaps from/to; timezone conversion bug makes a 23:00 `to` slip to the previous UTC day; copy-paste of the same date-time on both sides with millisecond differences.

Related errors


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