toeverything/AFFiNE · error · GraphqlBadRequest
calendar_provider_oauth_unsupported
calendar_provider_oauth_unsupported
Error message
GraphQL bad request, code: calendar_provider_oauth_unsupported, Selected calendar provider does not support OAuth.
What it means
Thrown by CalendarService.getAuthUrl when the requested calendar provider instance reports supportsOAuth = false. The OAuth login flow needs the provider to mint an authorization URL; providers that only do credential-based auth (e.g. CalDAV username/password) cannot serve this path, so the request is rejected as a bad request with code 'calendar_provider_oauth_unsupported'.
Source
Thrown at packages/backend/server/src/plugins/calendar/service.ts:607
if (this.canCreateNewAccounts(provider)) {
return true;
}
if (!userId) {
return false;
}
const accounts = await this.models.calendarAccount.listByUser(userId);
return accounts.some(account => account.provider === provider);
}
getAuthUrl(
provider: CalendarProviderName,
state: string,
redirectUri: string
) {
const instance = this.requireProvider(provider);
if (!instance.supportsOAuth) {
throw new GraphqlBadRequest({
code: 'calendar_provider_oauth_unsupported',
message: 'Selected calendar provider does not support OAuth.',
});
}
return instance.getAuthUrl(state, redirectUri);
}
private async assertCanPersistProviderAccount(
userId: string,
provider: CalendarProviderName,
providerAccountId: string
) {
if (this.canCreateNewAccounts(provider)) {
return;
}
const account = await this.models.calendarAccount.getByProviderAccount(
userId,View on GitHub (pinned to 591f874dad)
Solutions
- Route providers without OAuth to their own flow (CalDAV account discovery with username/password) instead of getAuthUrl.
- Before calling getAuthUrl, gate the UI on the provider's advertised capabilities (supportsOAuth) returned by the provider-list API.
- If you maintain the provider class and it does support OAuth, set supportsOAuth = true and implement getAuthUrl(state, redirectUri).
Example fix
// before
const url = await calendarService.getAuthUrl(CalendarProviderName.CalDAV, state, redirectUri);
// after
const instance = calendarService.getProvider(provider);
if (instance?.supportsOAuth) {
const url = await calendarService.getAuthUrl(provider, state, redirectUri);
} else {
openCredentialDialog(provider); // e.g. CalDAV discovery flow
} Defensive patterns
Strategy: type-guard
Type guard
function supportsOAuthFlow(
provider: CalendarProvider | undefined
): provider is CalendarProvider & { getAuthUrl: (state: string, redirectUri: string) => string } {
return !!provider && provider.supportsOAuth === true;
} Try / catch
try {
const url = calendarService.getAuthUrl(provider, state, redirectUri);
} catch (e) {
if (e?.extensions?.code === 'calendar_provider_oauth_unsupported') {
routeToCredentialFlow(provider);
} else throw e;
} Prevention
- Drive the connect flow off the provider's advertised capabilities, not a hardcoded provider list.
- Custom provider implementations must set supportsOAuth consistently with getAuthUrl availability.
- Hide OAuth buttons for providers known to be credential-only (e.g. CalDAV).
When it happens
Trigger: Calling the calendar OAuth URL query/mutation with provider = 'caldav' or any provider whose implementation sets supportsOAuth to false; a custom provider plugin that forgot to implement getAuthUrl/supportsOAuth; frontend routing a generic 'Connect calendar' button to the OAuth flow for all providers.
Common situations: UI shows one connect flow for every provider and picks OAuth for CalDAV; adding a new custom calendar provider without OAuth support but reusing the generic OAuth entry point; stale client logic after a provider dropped OAuth support.
Related errors
- missing_oauth_query_parameter
- unknown_oauth_provider
- oauth_state_expired
- caldav_provider_not_found
- caldav_provider_unavailable
AI-assisted analysis of toeverything/AFFiNE@591f874dad (2026-08-18).
Data as JSON: /api/errors/40a78fca1191e003.
Report an issue: GitHub.