calcom/cal.diy · error · UnauthorizedException
${googleCalendar.error?.message}
Error message
${googleCalendar.error?.message} What it means
Thrown by GoogleCalendarService.checkIfCalendarConnected (gcal.service.ts:122) as UnauthorizedException (HTTP 401) when the connected Google calendar object carries a truthy error.message produced by the upstream integration. The raw upstream message is re-thrown verbatim, so the actual cause (expired access token, quota, Google API error) is embedded in the response body. Mirror of error 363.
Source
Thrown at apps/api/v2/src/platform/calendars/services/gcal.service.ts:122
);
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,
accessToken: string,
origin: string,
redir?: string,
isDryRun?: boolean
) {
// User chose not to authorize your app or didn't authorize your app
// redirect directly without oauth code
if (!code || code === "undefined") {
return { url: redir || origin };
}
View on GitHub (pinned to 176037d0af)
Solutions
- Read the dynamic message in the 401 response body — it carries the integration's verbatim error text.
- If the message is auth/token-related, re-run the OAuth connect→save flow (see error 377).
- If the message indicates quota/transient failure, retry with backoff.
Example fix
// before: generic error handling
try { await api.get('/v2/calendars/google_calendar/check'); }
catch (e) { throw e; }
// after: branch on the upstream message
try { await api.get('/v2/calendars/google_calendar/check'); }
catch (e) {
const msg = e.response?.data?.message ?? '';
if (/token|credential|401|unauthorized/i.test(msg)) await reAuthGoogle();
else if (/quota|rate|retry/i.test(msg)) await retryWithBackoff(check);
else throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Inspect the upstream error from the list before calling check
const { data } = await api.get('/v2/calendars');
const gcal = data.connectedCalendars.find(c => c.integration?.type === 'google_calendar');
if (gcal?.error?.message) {
throw new UpstreamError(gcal.error.message);
} Type guard
function hasUpstreamError(conn) {
return typeof conn?.error?.message === 'string' && conn.error.message.length > 0;
} Try / catch
try {
await api.get('/v2/calendars/google_calendar/check');
} catch (e) {
const msg = e.response?.data?.message ?? '';
if (e.response?.status === 401) {
if (/token|credential|unauthorized|401/i.test(msg)) await reAuthGoogle();
else await retryWithBackoff(() => api.get('/v2/calendars/google_calendar/check'));
return;
}
throw e;
} Prevention
- Read the upstream message embedded in the 401 body — it is the real diagnosis.
- Retry transient/quota upstream errors with backoff; re-auth on token-related messages.
- Log the upstream error server-side for monitoring since the HTTP layer just forwards it.
When it happens
Trigger: Google Calendar API returned 401 because the access token expired and refresh failed; daily quota exceeded; transient Google API outage; revoked consent surfaced as an integration error.
Common situations: Expired access token; user revoked access in Google account; Google API rate limiting; intermittent failures under load.
Related errors
- ${appleCalendar.error?.message}
- No valid credentials available for Google Calendar
- Google Calendar credentials are invalid. Please reconnect.
- Google Calendar not connected.
- ${googleCalendar.error?.message}
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/6e0de3ad81e9bad4.
Report an issue: GitHub.