toeverything/AFFiNE · error · EmailVerificationRequired
email_verification_required
email_verification_required
Error message
You must verify your email before accessing this resource.
What it means
The sendChangePasswordEmail mutation requires the authenticated user to have emailVerified=true; if user.emailVerified is false, EmailVerificationRequired is thrown before any token is minted or email sent. This prevents unverified accounts from triggering password-change flows, ensuring the account's email ownership is established first.
Source
Thrown at packages/backend/server/src/core/auth/resolver.ts:170
await this.auth.sendNotificationChangeEmail(email);
return user;
}
@Mutation(() => Boolean)
async sendChangePasswordEmail(
@CurrentUser() user: CurrentUser,
@Args('callbackUrl') callbackUrl: string,
@Args('email', {
type: () => String,
nullable: true,
deprecationReason: 'fetched from signed in user',
})
_email: string | undefined,
@Context() context: GraphqlContext
) {
if (!user.emailVerified) {
throw new EmailVerificationRequired();
}
const { token, expiresAt } =
await this.models.verificationToken.createWithExpiresAt(
TokenType.ChangePassword,
user.id
);
const url = this.url.safeLink(callbackUrl, { userId: user.id, token });
return await this.auth.sendChangePasswordEmail(
user.email,
url,
this.mailMetadata(context, expiresAt)
);
}
@Mutation(() => Boolean)View on GitHub (pinned to 26c515e050)
Solutions
- Complete email verification first: call sendVerifyEmail and click the link to set emailVerified=true.
- If the user cannot verify (no access to inbox), an admin can mark the email verified out-of-band.
- Re-check that the user record's emailVerified column is set after verification completes.
- Guide the user through the verify-email flow before exposing the change-password UI.
Defensive patterns
Strategy: validation
Validate before calling
// guard the UI: only show 'change password' after email verification
if (!currentUser.emailVerified) {
showVerifyEmailFirst();
return;
} Type guard
function isEmailVerificationRequired(err: unknown): boolean {
return (
!!err &&
typeof err === 'object' &&
(err as { code?: string }).code === 'email_verification_required'
);
} Try / catch
try {
await sendChangePasswordEmail({ callbackUrl });
} catch (err) {
if (isEmailVerificationRequired(err)) {
redirectToVerifyEmail();
return;
}
throw err;
} Prevention
- Gate sensitive-flow UI behind a currentUser.emailVerified check.
- Drive users through email verification at sign-up.
- Have admins set emailVerified for accounts without inbox access.
- Re-check the flag after the verify-email step completes.
When it happens
Trigger: An authenticated but email-unverified user calls sendChangePasswordEmail. Common for accounts created via a method that skips verification, or that never completed the verify-email step.
Common situations: User signed up via a provider/admin path that didn't mark emailVerified. User hasn't clicked the verification email yet. Imported accounts where emailVerified wasn't set.
Related errors
AI-assisted analysis of toeverything/AFFiNE@26c515e050 (2026-08-12).
Data as JSON: /api/errors/46b7c214aae75642.
Report an issue: GitHub.