calcom/cal.diy · error · UnauthorizedException
No valid credentials available for Google Calendar
Error message
No valid credentials available for Google Calendar
What it means
Thrown by getAuthorizedCalendarInstance when neither delegated auth nor direct OAuth can produce a Google Calendar client. The method first attempts service-account delegation (getDelegatedCalendarInstance) when a userEmail and delegationCredential.id are supplied; if that returns null, it falls through and requires oAuthCredentials on the credential record. When credential.key is missing/empty the UnauthorizedException (HTTP 401) fires.
Source
Thrown at apps/api/v2/src/modules/cal-unified-calendars/services/google-calendar.service.ts:121
/**
* Gets an authorized Google Calendar instance
* Tries delegation credentials first, falls back to direct OAuth
*/
private async getAuthorizedCalendarInstance(
userEmail?: string,
oAuthCredentials?: Prisma.JsonValue | undefined,
delegationCredential?: { id: string } | null
): Promise<calendar_v3.Calendar> {
if (userEmail && delegationCredential?.id) {
const delegatedCalendar = await this.getDelegatedCalendarInstance(delegationCredential, userEmail);
if (delegatedCalendar) {
return delegatedCalendar;
}
}
// Fall back to direct OAuth authentication
if (!oAuthCredentials) {
throw new UnauthorizedException("No valid credentials available for Google Calendar");
}
const parsedOAuthCredentials = OAuth2UniversalSchema.parse(oAuthCredentials);
const oAuth2Client = await this.gCalService.getOAuthClient(this.gCalService.redirectUri);
oAuth2Client.setCredentials(parsedOAuthCredentials);
return new calendar_v3.Calendar({ auth: oAuth2Client });
}
private async getDelegatedCalendarInstance(
delegationCredential: { id: string },
emailToImpersonate: string
): Promise<calendar_v3.Calendar | null> {
try {
const oauthClientIdAliasRegex = /\+[a-zA-Z0-9]{25}/;
const cleanEmail = emailToImpersonate.replace(oauthClientIdAliasRegex, "");
const serviceAccountCreds =
await DelegationCredentialRepository.findByIdIncludeSensitiveServiceAccountKey({View on GitHub (pinned to 176037d0af)
Solutions
- Inspect the credential row (SELECT key, "delegationCredentialId", invalid FROM "AppCredential" WHERE id=?) and confirm whether key is populated or delegationCredentialId points to a live DelegationCredential with a serviceAccountKey.
- If the user is meant to use delegation, ensure the DelegationCredential row has a complete serviceAccountKey (client_email + private_key); otherwise re-run domain-wide delegation setup.
- If the user should use direct OAuth, have them reconnect Google Calendar so credential.key is populated with a valid OAuth token JSON.
- As a caller, pre-check that either credential.key is truthy or (credential.user.email and credential.delegationCredentialId) are set before invoking calendar operations.
Example fix
// before: caller blindly calls the service
const cal = await googleCalendarService.getCalendarClientForUser(userId);
// after: guard before calling
const cred = await credentialsRepository.findCredentialWithDelegationByTypeAndUserId(GOOGLE_CALENDAR_TYPE, userId);
if (!cred || cred.invalid || (!cred.key && !(cred.user?.email && cred.delegationCredentialId))) {
throw new UnauthorizedException('Reconnect Google Calendar before proceeding.');
} Defensive patterns
Strategy: validation
Validate before calling
// Validate a credential can yield a calendar client before calling the service.
function canAuthorizeCredential(cred: { key: unknown; user?: { email?: string | null }; delegationCredentialId?: string | null; invalid?: boolean }): boolean {
if (cred.invalid) return false;
const hasOAuth = Boolean(cred.key);
const hasDelegation = Boolean(cred.user?.email && cred.delegationCredentialId);
return hasOAuth || hasDelegation;
}
// usage
const cred = await credentialsRepository.findCredentialWithDelegationByTypeAndUserId(GOOGLE_CALENDAR_TYPE, userId);
if (!cred || !canAuthorizeCredential(cred)) {
throw new UnauthorizedException('Reconnect Google Calendar before proceeding.');
} Type guard
import type { AppCredential } from '@prisma/client';
function hasUsableOAuthKey(cred: AppCredential): boolean {
return cred.key != null && typeof cred.key === 'object' && Object.keys(cred.key).length > 0;
}
function hasDelegationContext(cred: AppCredential & { user?: { email?: string | null } }): boolean {
return Boolean(cred.delegationCredentialId && cred.user?.email);
}
function isCalendarClientReady(cred: AppCredential & { user?: { email?: string | null } }): cred is AppCredential & { user: { email: string } } {
return !cred.invalid && (hasUsableOAuthKey(cred) || hasDelegationContext(cred));
} Try / catch
try {
const cal = await googleCalendarService.getCalendarClientForUser(userId);
} catch (e) {
if (e instanceof UnauthorizedException && e.message.includes('No valid credentials')) {
// prompt reconnect; this is unrecoverable without user action
return { status: 'reconnect_required', provider: 'google_calendar' };
}
throw e;
} Prevention
- Before any calendar operation, fetch the credential once and reuse it; do not let the service re-fetch and fail late.
- When provisioning domain-wide delegation, write the serviceAccountKey atomically with the delegationCredential row.
- Run a periodic job that flags credentials whose delegationCredentialId points at a missing row.
- Treat a null credential.key as a hard error in onboarding, not a soft warning.
When it happens
Trigger: Calling getEventDetails/updateEventDetails with a booking reference whose credential.key is null, or calling getCalendarClientForUser/getCalendarClientByCredentialId for a credential that has a delegationCredentialId pointing at a delegation record whose serviceAccountKey is missing (getDelegatedCalendarInstance swallows the error and returns null) and whose own key field is also empty.
Common situations: A credential row was created for delegation but the service-account JSON was never stored; the delegation credential was deleted out of band but delegationCredentialId on the credential still points at it; a migration left credential.key null for users moved to domain-wide delegation.
Related errors
- Google Calendar credentials are invalid. Please reconnect.
- Google Calendar is not connected for this user
- Calendar credentials are invalid. Please reconnect.
- ${googleCalendar.error?.message}
- Calendar connection not found
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/dccc9ff6f78edced.
Report an issue: GitHub.