calcom/cal.diy · error · BadRequestException

Could not adjust timezone for slot ${slot.time} with timezon

Error message

Could not adjust timezone for slot ${slot.time} with timezone ${timeZone}

What it means

A NestJS BadRequestException (HTTP 400) from SlotsOutputService_2024_09_04.getAvailableTimeSlots. When a timeZone is supplied, DateTime.fromISO(slot.time,{zone:'utc'}).setZone(timeZone).toISO() returned null. This means either the requested timeZone is not recognized by luxon or the internal slot.time value is corrupt. Format is the default 'time' (non-range) slot output.

Source

Thrown at apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots-output.service.ts:65

    const slots: { [key: string]: (Slot_2024_09_04 | SeatedSlot_2024_09_04)[] } = {};
    for (const date in availableSlots.slots) {
      const availableTimeSlots = availableSlots.slots[date].filter((slot) => !slot.away);
      if (availableTimeSlots.length > 0) {
        slots[date] = availableTimeSlots.map((slot) => {
          if (!timeZone) {
            if (!eventType?.seatsPerTimeSlot) {
              return this.getAvailableTimeSlot(slot.time);
            }
            return this.getAvailableTimeSlotSeated(
              slot.time,
              slot.attendees || 0,
              eventType.seatsPerTimeSlot || 0,
              slot.bookingUid
            );
          }
          const slotTimezoneAdjusted = DateTime.fromISO(slot.time, { zone: "utc" }).setZone(timeZone).toISO();
          if (!slotTimezoneAdjusted) {
            throw new BadRequestException(
              `Could not adjust timezone for slot ${slot.time} with timezone ${timeZone}`
            );
          }
          if (!eventType?.seatsPerTimeSlot) {
            return this.getAvailableTimeSlot(slotTimezoneAdjusted);
          }
          return this.getAvailableTimeSlotSeated(
            slotTimezoneAdjusted,
            slot.attendees || 0,
            eventType.seatsPerTimeSlot || 0,
            slot.bookingUid
          );
        });
      }
    }

    return slots;
  }

View on GitHub (pinned to 176037d0af)

Solutions

  1. Use canonical IANA timezone names (e.g. 'Europe/Berlin', 'America/New_York') and validate with Intl.supportedValuesOf('timeZone') or luxon's DateTime.now().setZone(tz).isValid before the request.
  2. Trim and verify the timeZone string is non-empty.
  3. If you don't need conversion, omit timeZone entirely to fall back to UTC output.
  4. On the server, ensure the luxon/full-icu data is available so all IANA zones resolve.

Example fix

// before
fetch(`/v2/slots?...&timeZone=${tz}`)  // tz = 'EST'

// after
const valid = DateTime.now().setZone(tz).isValid;
if (!valid) throw new ClientError(`Unsupported timezone: ${tz}`);
fetch(`/v2/slots?...&timeZone=${encodeURIComponent(tz)}`);
Defensive patterns

Strategy: validation

Validate before calling

import { DateTime } from 'luxon';

function assertValidTimezone(tz: unknown): string | undefined {
  if (tz === undefined || tz === null || tz === '') return undefined;
  if (typeof tz !== 'string') throw new TypeError('timeZone must be a string');
  const probe = DateTime.now().setZone(tz);
  if (!probe.isValid) throw new RangeError(`Unsupported timeZone: ${tz} (${probe.invalidReason})`);
  return tz;
}
const timeZone = assertValidTimezone(input.timeZone);

Type guard

function isIANATimezone(v: unknown): v is string {
  if (typeof v !== 'string' || v.trim() === '') return false;
  return DateTime.now().setZone(v).isValid;
}

Try / catch

try {
  await cal.slots.list({ ..., timeZone });
} catch (e) {
  if (e instanceof HttpError && e.statusCode === 400 && /timezone/i.test(e.message)) {
    // retry once in UTC, or re-prompt for a valid zone
    return cal.slots.list({ ..., /* timeZone omitted */ });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling GET /v2/slots/2024-09-04 with a `timeZone` query param that luxon cannot resolve (typo like 'Europ/Berlin', an IANA name that is not installed, or a non-IANA abbreviation like 'EST' that luxon does not accept). Less commonly, the upstream availableSlots service returned a slot.time that is not a valid ISO string.

Common situations: Using browser Intl.timezone that returns an empty string; passing 'UTC'/'GMT' abbreviations; user-supplied timezone from a free-text field; outdated luxon timezone database on the server.

Related errors


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