calcom/cal.diy · error · BadRequestException

Credentials for google_calendar not found.

Error message

Credentials for google_calendar not found.

What it means

Thrown by GoogleCalendarService.checkIfCalendarConnected (gcal.service.ts:107) as BadRequestException (HTTP 400) when findCredentialByTypeAndUserId('google_calendar', userId) returns null — no Google Calendar credential stored for the user. Semantic mismatch like error 360: a 'not found' condition reported as 400. Reached via the google_calendar 'check' endpoint.

Source

Thrown at apps/api/v2/src/platform/calendars/services/gcal.service.ts:107

    if (!app) {
      throw new NotFoundException();
    }

    const { client_id, client_secret } = this.gcalResponseSchema.parse(app.keys);

    const oAuth2Client = new OAuth2Client(client_id, client_secret, redirectUri);
    return oAuth2Client;
  }

  async checkIfCalendarConnected(userId: number): Promise<{ status: typeof SUCCESS_STATUS }> {
    const gcalCredentials = await this.credentialRepository.findCredentialByTypeAndUserId(
      "google_calendar",
      userId
    );

    if (!gcalCredentials) {
      throw new BadRequestException("Credentials for google_calendar not found.");
    }

    if (gcalCredentials.invalid) {
      throw new BadRequestException("Invalid google OAuth credentials.");
    }

    const { connectedCalendars } = await this.calendarsService.getCalendars(userId);
    const googleCalendar = connectedCalendars.find(
      (cal: { integration: { type: string } }) => cal.integration.type === GOOGLE_CALENDAR_TYPE
    );
    if (!googleCalendar) {
      throw new UnauthorizedException("Google Calendar not connected.");
    }
    if (googleCalendar.error?.message) {
      throw new UnauthorizedException(googleCalendar.error?.message);
    }

    return { status: SUCCESS_STATUS };

View on GitHub (pinned to 176037d0af)

Solutions

  1. Complete the Google OAuth flow first: connect (get authUrl) → user authorizes → save (callback) → then check.
  2. Confirm the credential row exists: SELECT id FROM "Credential" WHERE type='google_calendar' AND "userId"=<id>.
  3. If invalid=true (error 377), re-run the OAuth flow.

Example fix

// before: checking before OAuth completes
await api.get('/v2/calendars/google_calendar/check'); // 400

// after: complete connect → save → check
const { data } = await api.post('/v2/calendars/google_calendar/connect', {});
// user visits data.authUrl, authorizes, returns with ?code=
await api.post('/v2/calendars/google_calendar/save', { code, ... });
await api.get('/v2/calendars/google_calendar/check'); // 200
Defensive patterns

Strategy: validation

Validate before calling

// Confirm a google credential exists before calling check
const { data } = await api.get('/v2/calendars');
const hasGoogle = data.connectedCalendars.some(c => c.integration?.type === 'google_calendar');
if (!hasGoogle) {
  // route the user to OAuth connect instead of calling check
  return redirectToGoogleConnect();
}

Type guard

function hasGoogleCredential(list) {
  return Array.isArray(list) && list.some(c => c?.integration?.type === 'google_calendar');
}

Try / catch

try {
  await api.get('/v2/calendars/google_calendar/check');
} catch (e) {
  if (e.response?.status === 400 && /not found/i.test(e.response?.data?.message)) {
    // not yet connected — start OAuth rather than retry
    return startGoogleOAuth();
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling check for google_calendar before completing the OAuth connect→save flow; user never authorized Google; credential was disconnected/deleted.

Common situations: OAuth callback not yet processed; onboarding polling check before save completes; user revoked and the credential row was removed.

Related errors


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