toeverything/AFFiNE · error · MissingOauthQueryParameter
missing_oauth_query_parameter
missing_oauth_query_parameter
Error message
Missing query parameter `provider`.
What it means
MissingOauthQueryParameter({name:'provider'}) thrown at packages/backend/server/src/plugins/oauth/service.ts:119 when the server-side OAuthState record loaded from the challenge store has no provider field. Normally preflight always persists provider alongside client/clientNonce/pkce, so this indicates a corrupted, legacy, or hand-crafted state record rather than a client input problem.
Source
Thrown at packages/backend/server/src/plugins/oauth/service.ts:119
if (!state.token) state.token = stateStr;
if (
state.provider === OAuthProviderName.Apple &&
rawState &&
state.client &&
state.client !== 'web'
) {
return {
type: 'handoff',
code: input.code,
provider: rawState.provider,
state,
stateToken: stateStr,
};
}
if (!state.provider) {
throw new MissingOauthQueryParameter({ name: 'provider' });
}
const provider = this.providerFactory.get(state.provider);
if (!provider) {
throw new UnknownOauthProvider({ name: state.provider ?? 'unknown' });
}
if (
state.provider !== OAuthProviderName.Apple &&
(!input.clientNonce ||
!state.clientNonce ||
state.clientNonce !== input.clientNonce)
) {
throw new InvalidAuthState();
}
return {View on GitHub (pinned to b4c8548c09)
Solutions
- Start a new login flow (new preflight writes a complete state record).
- If it persists, flush the stale oauth_state entries from Redis/cache after upgrading.
- Audit custom code that calls saveOAuthState() — it must include provider: OAuthProviderName.
- Check that all replicas run the same backend version so state payloads have a consistent shape.
Example fix
// before (custom code)
await this.oauth.saveOAuthState({ client: 'web', redirectUri: '/' } as OAuthState);
// after
await this.oauth.saveOAuthState({ provider: OAuthProviderName.OIDC, client: 'web', redirectUri: '/' }); Defensive patterns
Strategy: try-catch
Type guard
function isCompleteState(state: OAuthState | null): state is OAuthState {
return !!state && typeof state.provider === 'string' && state.provider.length > 0;
} Try / catch
try {
await oauth.verifyCallback(input);
} catch (err) {
if (err instanceof MissingOauthQueryParameter && err.data?.name === 'provider') {
// corrupted/legacy state record: flush stale oauth_state cache entries, restart login
}
} Prevention
- Always include provider when calling saveOAuthState().
- Flush stale oauth_state cache entries when upgrading server versions.
- Keep all replicas on the same version so state payloads stay shape-compatible.
When it happens
Trigger: The oauth_state cache entry was written without a provider (custom code calling saveOAuthState with an incomplete object); state records created by an older server version being replayed after upgrade; direct calls to OAuthService.verifyCallback with a stateStr whose stored payload lacks provider.
Common situations: Version upgrade where the state payload schema changed and old cached states are still in Redis; a plugin or fork writing states directly into the challenge store; cache key collisions overwriting an oauth_state entry with an incomplete object.
Related errors
- unknown_oauth_provider
- unknown_oauth_provider
- oauth_state_expired
- invalid_oauth_response
- sign_up_forbidden
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/1959b2a474514ad6.
Report an issue: GitHub.