toeverything/AFFiNE · error · UnknownOauthProvider
unknown_oauth_provider
unknown_oauth_provider
Error message
Unknown authentication provider ${name}. What it means
Thrown at controller.ts:46 when CalendarService.isProviderAvailableFor(providerName, { oauth: true }) returns false. That method (service.ts:561) returns false for two distinct reasons: (a) no provider is registered under that name in CalendarProviderFactory's map, or (b) the provider is registered but its supportsOAuth flag is false. Only providers in the CalendarProviderName enum ('google', 'caldav') are recognized, and only those with supportsOAuth=true pass the oauth check. It is a user-facing invalid_input error (UnknownOauthProvider, code 'unknown_oauth_provider').
Source
Thrown at packages/backend/server/src/plugins/calendar/controller.ts:46
constructor(
private readonly calendar: CalendarService,
private readonly oauth: CalendarOAuthService,
private readonly url: URLHelper
) {}
@Post('/oauth/preflight')
@HttpCode(HttpStatus.OK)
async preflight(
@CurrentUser() user: CurrentUser,
@Body('provider') providerName?: CalendarProviderName,
@Body('redirect_uri') redirectUri?: string
) {
if (!providerName) {
throw new MissingOauthQueryParameter({ name: 'provider' });
}
if (!this.calendar.isProviderAvailableFor(providerName, { oauth: true })) {
throw new UnknownOauthProvider({ name: providerName });
}
await this.calendar.assertCanLinkProvider(user.id, providerName);
const state = await this.oauth.saveOAuthState({
provider: providerName,
userId: user.id,
redirectUri,
});
const callbackUrl = this.calendar.getCallbackUrl();
const authUrl = this.calendar.getAuthUrl(providerName, state, callbackUrl);
return { url: authUrl };
}
@Public()
@Get('/oauth/callback')View on GitHub (pinned to 26c515e050)
Solutions
- Send a provider value that is both a valid CalendarProviderName enum member AND supports OAuth — currently 'google'. Use the exact lowercase string.
- If you intend CalDAV, use the CalDAV credential-linking flow instead of the OAuth preflight, since CalDAV does not support OAuth.
- Check server logs at boot for 'Calendar provider [google] registered.' — if missing, the Google provider module is disabled or failed to load; re-enable it in config (AFFiNE calendar.google.* settings).
- Before calling preflight, query the available providers (resolver exposes them via the providers field on the calendar resolver) and only offer OAuth for those that report supportsOAuth=true.
Example fix
// before
const res = await fetch('/api/calendar/oauth/preflight', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ provider: 'caldav' }), // CalDAV has no OAuth
});
// after — use an OAuth-capable provider, lowercase
const res = await fetch('/api/calendar/oauth/preflight', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ provider: 'google' }),
}); Defensive patterns
Strategy: type-guard
Validate before calling
// Resolve which providers actually support OAuth before offering the flow.
import { CalendarProviderName } from './providers';
// 'caldav' uses credentials, not OAuth — do not send it to /oauth/preflight.
const OAUTH_PROVIDERS: ReadonlyArray<CalendarProviderName> = [CalendarProviderName.Google];
function pickOAuthProvider(name: string) {
const normalized = name.toLowerCase();
if (!OAUTH_PROVIDERS.includes(normalized as CalendarProviderName)) {
throw new Error(`Provider '${name}' does not support OAuth. Use ${OAUTH_PROVIDERS.join(', ')}.`);
}
return normalized as CalendarProviderName;
}
const provider = pickOAuthProvider(userSelection); Type guard
import { CalendarProviderName } from './providers';
const OAUTH_CAPABLE: ReadonlySet<CalendarProviderName> = new Set([CalendarProviderName.Google]);
function supportsOAuth(value: unknown): value is CalendarProviderName {
return typeof value === 'string'
&& Object.values(CalendarProviderName).includes(value as CalendarProviderName)
&& OAUTH_CAPABLE.has(value as CalendarProviderName);
} Try / catch
try {
await startCalendarOAuth(providerName);
} catch (err) {
if (err instanceof Error && err.message.includes('unknown_oauth_provider')) {
notifyUser(`'${providerName}' is not an available OAuth provider.`);
} else {
throw err;
}
} Prevention
- Fetch the server's advertised provider list at app start and only render OAuth buttons for providers whose supportsOAuth flag is true.
- Use the lowercase enum string values ('google', 'caldav') verbatim — never capitalize or rename client-side.
- Treat CalDAV as credentials-only; route it through the credential-linking API, not the OAuth preflight.
- After a server upgrade, re-query the provider list in case a provider was added or OAuth support toggled.
When it happens
Trigger: POST /api/calendar/oauth/preflight with body { "provider": "caldav" } when CalDAV's supportsOAuth is false (it uses credential-based auth, not OAuth); or { "provider": "outlook" } / any string not in the enum, because providerFactory.get() returns undefined; or a typo like { "provider": "Google" } (capitalized) since the enum values are lowercase. The guard fires before OAuth state is saved.
Common situations: Client hardcodes a provider name that was removed or renamed in a server upgrade; provider plugins not registered at boot (e.g. Google provider module disabled via config so the factory map is empty); attempting the OAuth flow for CalDAV which is credentials-only; case mismatch between the enum ('google') and what the client sends.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- missing_oauth_query_parameter
- action_forbidden
- space_access_denied
- doc_action_denied
- space_access_denied
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/5f1cd75784037f34.
Report an issue: GitHub.