calcom/cal.diy · error · BadRequestException
Invalid time range given - check the 'start' and 'end' query
Error message
Invalid time range given - check the 'start' and 'end' query parameters.
What it means
A NestJS BadRequestException (HTTP 400) from SlotsService_2024_09_04.fetchAndFormatSlots. It wraps any error from AvailableSlotsService whose message includes 'Invalid time range given' into a stable client-facing message pointing at the start/end query params. The underlying cause is that the availability engine rejected the window — typically start is on or after end, or the range exceeds the allowed lookup span.
Source
Thrown at apps/api/v2/src/modules/slots/slots-2024-09-04/services/slots.service.ts:76
try {
const availableSlots: TimeSlots = await this.availableSlotsService.getAvailableSlots({
input: queryTransformed,
ctx: {},
});
const formatted = await this.slotsOutputService.getAvailableSlots(
availableSlots,
queryTransformed.eventTypeId,
queryTransformed.duration,
format,
queryTransformed.timeZone
);
return formatted;
} catch (error) {
if (error instanceof Error) {
if (error.message.includes("Invalid time range given")) {
throw new BadRequestException(
"Invalid time range given - check the 'start' and 'end' query parameters."
);
}
}
throw error;
}
}
async getAvailableSlots(query: GetSlotsInput_2024_09_04) {
const queryTransformed = await this.slotsInputService.transformGetSlotsQuery(query);
return this.fetchAndFormatSlots(queryTransformed, query.format);
}
async getAvailableSlotsWithRouting(query: GetSlotsInputWithRouting_2024_09_04) {
const queryTransformed = await this.slotsInputService.transformRoutingGetSlotsQuery(query);
return this.fetchAndFormatSlots(queryTransformed, query.format);
}
View on GitHub (pinned to 176037d0af)
Solutions
- Ensure end is strictly greater than start before the request.
- Cap the window to the platform's allowed lookup span (default is typically 30 days; trim end accordingly).
- When offering a date range UI, enforce start <= end and a max span client-side.
- If you need a longer view, page through multiple requests with offsets.
Example fix
// before
const start = DateTime.now().toISO();
const end = DateTime.now().toISO(); // same → invalid
// after
const start = DateTime.now().toISO()!;
const end = DateTime.now().plus({ days: 30 }).toISO()!;
fetch(`/v2/slots?start=${encodeURIComponent(start)}&end=${encodeURIComponent(end)}`); Defensive patterns
Strategy: validation
Validate before calling
import { DateTime } from 'luxon';
const MAX_LOOKUP_DAYS = 30;
function assertValidRange(startISO: unknown, endISO: unknown): { start: string; end: string } {
const start = DateTime.fromISO(String(startISO), { zone: 'utc' });
const end = DateTime.fromISO(String(endISO), { zone: 'utc' });
if (!start.isValid || !end.isValid) throw new RangeError('start/end must be valid ISO');
if (end.toMillis() <= start.toMillis()) throw new RangeError('end must be strictly after start');
if (end.diff(start, 'days').days > MAX_LOOKUP_DAYS) throw new RangeError(`window must be <= ${MAX_LOOKUP_DAYS} days`);
return { start: start.toISO()!, end: end.toISO()! };
} Type guard
function isValidRange(startISO: unknown, endISO: unknown): startISO is string {
if (typeof startISO !== 'string' || typeof endISO !== 'string') return false;
const s = DateTime.fromISO(startISO, { zone: 'utc' });
const e = DateTime.fromISO(endISO, { zone: 'utc' });
return s.isValid && e.isValid && e.toMillis() > s.toMillis();
} Try / catch
try {
await cal.slots.list({ start, end, ... });
} catch (e) {
if (e instanceof HttpError && e.statusCode === 400 && /Invalid time range/i.test(e.message)) {
// correct the window and retry once
const { start: s, end: en } = clampWindow(start, end);
return cal.slots.list({ start: s, end: en, ... });
}
throw e;
} Prevention
- Enforce start < end on the client before the request.
- Cap the lookup window to the platform maximum (default ~30 days).
- Page long ranges with multiple smaller requests.
- Re-derive end from start + N days rather than caching it.
When it happens
Trigger: GET /v2/slots where start >= end; start and end are identical; the window (end - start) exceeds the configured maximum slot lookup range (often 30/45/180 days); start/end are valid ISO but semantically inverted.
Common situations: Defaulting end to the same value as start; flipping date pickers; requesting a full year of slots; timezone conversion on the client pushing start past end; reusing a cached end that is now in the past.
Related errors
- Invalid time range given - check the 'startTime' and 'endTim
- Invalid start date
- Invalid end date
- Could not adjust timezone for slot ${slot.time} with timezon
- Could not adjust timezone for slot end time ${slot.time} wit
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/6c6e2acdfb249d97.
Report an issue: GitHub.