calcom/cal.diy · warning · BadRequestException

Event operations for this connection are currently only avai

Error message

Event operations for this connection are currently only available for Google Calendar

What it means

Thrown by getCalendarClientByCredentialId when the resolved credential's type is not google_calendar. The connection-scoped event endpoints currently only implement Google Calendar, so an Office 365 or Apple credential is rejected with HTTP 400. This guards the connection-scoped path before attempting event CRUD.

Source

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

    }
    return this.getAuthorizedCalendarInstance(
      credential.user?.email ?? undefined,
      credential.key,
      credential.delegationCredentialId ? { id: credential.delegationCredentialId } : null
    );
  }

  /**
   * Gets an authorized Google Calendar instance for a specific credential (connection).
   * Tries delegated auth first (if available), then falls back to direct OAuth.
   */
  async getCalendarClientByCredentialId(userId: number, credentialId: number): Promise<calendar_v3.Calendar> {
    const credential = await this.credentialsRepository.findCredentialByIdAndUserId(credentialId, userId);
    if (!credential) {
      throw new NotFoundException("Calendar connection not found");
    }
    if (credential.type !== GOOGLE_CALENDAR_TYPE) {
      throw new BadRequestException(
        "Event operations for this connection are currently only available for Google Calendar"
      );
    }
    if (credential.invalid) {
      throw new UnauthorizedException("Calendar credentials are invalid. Please reconnect.");
    }
    return this.getAuthorizedCalendarInstance(
      credential.user?.email ?? undefined,
      credential.key,
      credential.delegationCredentialId ? { id: credential.delegationCredentialId } : null
    );
  }

  // ─── Shared private helpers (DRY calendar CRUD) ──────────────────────

  private async listEventsWithClient(
    calendar: calendar_v3.Calendar,
    calendarId: string,

View on GitHub (pinned to 176037d0af)

Solutions

  1. Filter the connections list to type === google_calendar before offering event operations in the UI.
  2. Call the connection list endpoint and map each to its supported capability set, disabling event actions for non-Google connections.
  3. Track the roadmap for Office 365/Apple event support and surface an 'unsupported' state rather than issuing the request.
  4. If Google is required, have the user connect Google Calendar and use that connectionId.

Example fix

// before: client iterates all connections
for (const c of connections) {
  await api.createEvent(c.connectionId, body);
}

// after
for (const c of connections) {
  if (c.type !== 'google_calendar') continue; // skip unsupported providers
  await api.createEvent(c.connectionId, body);
}
Defensive patterns

Strategy: type-guard

Validate before calling

import { GOOGLE_CALENDAR_TYPE } from '@calcom/platform-constants';

async function getGoogleConnectionForUser(userId: number): Promise<{ id: number }> {
  const cred = await credentialsRepository.findCredentialByIdAndUserId(/* candidate */, userId);
  if (!cred || cred.type !== GOOGLE_CALENDAR_TYPE) {
    throw new BadRequestException('Operation requires a Google Calendar connection.');
  }
  return { id: cred.id };
}

Type guard

import { GOOGLE_CALENDAR_TYPE } from '@calcom/platform-constants';

function isGoogleCalendarType(c: { type: string } | null): c is { type: 'google_calendar' } {
  return c !== null && c.type === GOOGLE_CALENDAR_TYPE;
}

Try / catch

try {
  await googleCalendarService.getEventByConnectionId(userId, credentialId, calId, evId);
} catch (e) {
  if (e instanceof BadRequestException && /only available for Google Calendar/.test(e.message)) {
    return res.status(400).json({ code: 'unsupported_provider', supported: ['google_calendar'] });
  }
  throw e;
}

Prevention

When it happens

Trigger: Client passes a connectionId that resolves to an office_365_calendar or apple_calendar credential into the events endpoints (e.g. POST /v2/calendars/connections/{id}/events). Also happens if a future credential type slug is used.

Common situations: UI lists all connections uniformly but only Google supports event ops; user picks an Outlook connection from a dropdown; integration logic iterates all connections without filtering by type.

Related errors


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