calcom/cal.diy · error · BadRequestException

Invalid google OAuth credentials.

Error message

Invalid google OAuth credentials.

What it means

Thrown by GoogleCalendarService.checkIfCalendarConnected (gcal.service.ts:111) as BadRequestException (HTTP 400) when the google_calendar credential row exists but its 'invalid' flag is true. A prior token-refresh or API call failed and flagged the credential; Google OAuth refresh tokens also expire after 6 months of inactivity.

Source

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

    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 };
  }

  async saveCalendarCredentialsAndRedirect(
    code: string,

View on GitHub (pinned to 176037d0af)

Solutions

  1. Re-run the Google OAuth connect→save flow, which generates a fresh credential and clears invalid (prompt:'consent' is already set to force new tokens).
  2. If you changed OAuth scopes, verify the new scopes match CALENDAR_SCOPES in gcal.service.ts.
  3. After save, confirm invalid=false before retrying check.

Example fix

// before: re-checking a flagged credential
await api.get('/v2/calendars/google_calendar/check'); // 400 'Invalid google OAuth credentials.'

// after: force a fresh consent flow
const { data } = await api.post('/v2/calendars/google_calendar/connect', {});
// user re-authorizes at data.authUrl (prompt=consent)
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

// Detect an invalid google credential via the list before check
const { data } = await api.get('/v2/calendars');
const gcal = data.connectedCalendars.find(c => c.integration?.type === 'google_calendar');
if (gcal?.error || gcal === undefined) {
  // invalid or missing — re-run consent
  await startGoogleOAuth();
}

Type guard

function isCredentialValid(cred) {
  return !!cred && cred.invalid === false;
}

Try / catch

try {
  await api.get('/v2/calendars/google_calendar/check');
} catch (e) {
  if (e.response?.status === 400 && /invalid/i.test(e.response?.data?.message)) {
    // force fresh consent (prompt=consent is already set server-side)
    await startGoogleOAuth();
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: User revoked access from their Google account security page; refresh token expired due to long inactivity; OAuth scopes changed requiring re-consent; a background refresh failure set the flag.

Common situations: Revoked app access; dormant user returning after months; Google project credential rotation.

Related errors


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