calcom/cal.diy · error · BadRequestException

Failed to create calendar event

Error message

Failed to create calendar event

What it means

Thrown by createEventWithClient when calendar.events.insert resolves but response.data is falsy — Google accepted the request but returned an empty body. This is a defensive guard before the success-path cast; the surrounding catch maps any thrown Google API error to the same message via mapGoogleApiError. Returns HTTP 400.

Source

Thrown at apps/api/v2/src/modules/cal-unified-calendars/services/google-calendar.service.ts:269

        timeZone: body.start.timeZone,
      },
      end: {
        dateTime: body.end.time,
        timeZone: body.end.timeZone,
      },
      attendees: body.attendees?.map((a) => ({
        email: a.email,
        displayName: a.name,
      })),
    };
    try {
      const response = await calendar.events.insert({
        calendarId: effectiveCalendarId,
        requestBody,
        sendUpdates: "none",
      });
      if (!response.data) {
        throw new BadRequestException("Failed to create calendar event");
      }
      return response.data as GoogleCalendarEventResponse;
    } catch (error) {
      if (error instanceof HttpException) throw error;
      throw this.mapGoogleApiError(error, "Failed to create calendar event");
    }
  }

  private async getEventWithClient(
    calendar: calendar_v3.Calendar,
    calendarId: string,
    eventId: string
  ): Promise<GoogleCalendarEventResponse> {
    const effectiveCalendarId = calendarId || "primary";
    try {
      const event = await calendar.events.get({
        calendarId: effectiveCalendarId,
        eventId,

View on GitHub (pinned to 176037d0af)

Solutions

  1. Inspect the full error in server logs — if it came from mapGoogleApiError, the underlying GaxiosError has the Google reason; capture error.response.data.error.errors for the precise cause.
  2. Validate the calendarId belongs to the authenticated user and is writable.
  3. Retry once for transient empty-body responses; if it persists, file a Google API issue with the request id.
  4. Sanitize the event payload (title length, attendee email format, timezone IANA names) before insert.

Example fix

// before
const response = await calendar.events.insert({ calendarId, requestBody });
if (!response.data) {
  throw new BadRequestException('Failed to create calendar event');
}

// after: log the underlying detail for diagnostics
if (!response.data) {
  this.logger.error('Google insert returned empty body', { calendarId, status: response.status });
  throw new InternalServerErrorException('Google Calendar returned an unexpected empty response');
}
Defensive patterns

Strategy: try-catch

Validate before calling

function validateCreateEventInput(body: CreateUnifiedCalendarEventInput): string[] {
  const errors: string[] = [];
  if (!body.title || body.title.length > 1024) errors.push('title required, max 1024 chars');
  if (!body.start?.time || !body.end?.time) errors.push('start.time and end.time required');
  if (body.attendees) {
    for (const a of body.attendees) {
      if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(a.email)) errors.push(`invalid attendee email: ${a.email}`);
    }
  }
  return errors;
}

const errs = validateCreateEventInput(body);
if (errs.length) throw new BadRequestException(errs.join('; '));

Type guard

function isGoogleInsertSuccess(r: { data?: unknown }): r is { data: Record<string, unknown> } {
  return Boolean(r && typeof r.data === 'object' && r.data !== null && 'id' in (r.data as object));
}

Try / catch

try {
  return await googleCalendarService.createEventForUser(userId, calId, body);
} catch (e) {
  if (e instanceof BadRequestException && /Failed to create/.test(e.message)) {
    // surface generic failure; log correlation id for ops
    logger.error('create event failed', { userId, calId, body });
    throw new ApiError('event_create_failed', 502);
  }
  throw e;
}

Prevention

When it happens

Trigger: Google Calendar API returns 2xx with an empty body (rare — usually a partial outage or intermediate proxy stripping the body), or a malformed calendarId that the API silently no-ops on. More commonly this exact message surfaces when the insert call itself throws and mapGoogleApiError maps a 400 to BadRequestException with the fallback string.

Common situations: calendarId references a calendar the user can write metadata about but not insert into; transient Google API quirk; an API gateway or proxy between the service and Google that rewrites responses; time-zone or attendee payload that triggers a soft 400.

Related errors


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