toeverything/AFFiNE · error · SignUpForbidden
sign_up_forbidden
sign_up_forbidden
Error message
You are not allowed to sign up.
What it means
MagicLinkService.assertSignupAllowed runs when the email has no existing user. If config.auth.allowSignup is false (self-hosted instances after the first user, or signup disabled by policy), it throws SignUpForbidden (sign_up_forbidden) - passwordless sign-in cannot silently create accounts when signup is closed.
Solutions
- An administrator enables signup in auth config, or invites the user from workspace member management
- Sign in with an existing account instead
- Double-check the email address - a typo makes an existing user look like a new signup
- Self-hosters: verify the allowSignup setting and licensing/user-limit state
Defensive patterns
Strategy: try-catch
Type guard
function isSignUpForbidden(e: unknown): boolean {
return typeof e === 'object' && e !== null && (e as { code?: string }).code === 'sign_up_forbidden';
} Try / catch
try {
await sendMagicLink(email);
} catch (e) {
if (isSignUpForbidden(e)) {
show('Sign-up is disabled on this instance. Ask an admin to invite you.');
} else throw e;
} Prevention
- Surface instance signup policy in the UI before users attempt sign-up
- Prefer admin-invite flows on closed-signup instances
When it happens
Trigger: A brand-new email requests a magic link on an instance where signup is disabled; the workspace is at its licensed user cap with signup turned off; admins closed signup after onboarding and a new contractor tries the flow.
Common situations: Self-hosted AFFiNE where the first account already exists and allowSignup defaults off; enterprise deployments with invite-only policy; users mistyping their email so they look 'new' to the system.
Related errors
- invalid_email
- email_already_used
- email_service_not_configured
- search_provider_not_found
- action_forbidden
AI-assisted analysis of toeverything/AFFiNE@b4c8548c09 (2026-08-18).
Data as JSON: /api/errors/95dcf65d41d3ae56.
Report an issue: GitHub.
Appendix: source
Thrown at packages/backend/server/src/core/auth/magic-link.ts:135
TokenType.SignIn,
consumed.token,
{
credential: email,
}
);
if (!tokenRecord) {
throw new InvalidEmailToken();
}
const user = await this.models.user.fulfill(email);
return { userId: user.id, method: 'magic_link' };
}
private async assertSignupAllowed(email: string) {
if (!this.config.auth.allowSignup) {
throw new SignUpForbidden();
}
if (!this.config.auth.requireEmailDomainVerification) {
return;
}
if (!(await verifyEmailDomainRecords(email))) {
throw new InvalidEmail({ email });
}
}
}
View on GitHub (pinned to b4c8548c09)