coleam00/Archon · warning · APIError
Signup is disabled.
Error message
Signup is disabled.
What it means
Archon's auth instance (better-auth style) throws this 403 APIError from the signup hook when new-user registration is disabled. The server supports signup modes (open / allowlist / disabled); in 'disabled' mode the primary gate is the `disableSignUp` flag passed to the auth library, and this hook re-checks `signupDisabled` as defense in depth so the hook remains correct even if upstream enforcement changes. It fires after the auth library has already accepted a registration attempt but before the user record is returned.
Source
Thrown at packages/server/src/auth/instance.ts:115
// `disabled` (no allowlist + no ARCHON_AUTH_OPEN_SIGNUP=true). The allowlist
// hook below is the belt-and-suspenders for `allowlist` mode.
emailAndPassword: { enabled: true, disableSignUp: signupDisabled },
user: { modelName: 'remote_agent_auth_user' },
session: { modelName: 'remote_agent_auth_session' },
account: { modelName: 'remote_agent_auth_account' },
verification: { modelName: 'remote_agent_auth_verification' },
databaseHooks: {
user: {
create: {
before: async (
user: User & Record<string, unknown>
): Promise<{ data: User & Record<string, unknown> }> => {
// Defense in depth: `disableSignUp` (set above from getSignupMode)
// already blocks registration in `disabled` mode before this hook
// runs — re-check here so the hook stays correct on its own if that
// upstream enforcement ever changes.
if (signupDisabled) {
throw new APIError('FORBIDDEN', { message: 'Signup is disabled.' });
}
// Invite gate (`allowlist` mode): reject signups whose email is not on
// the allowlist. Throwing APIError surfaces a clean 403 instead of a
// generic 500. An empty allowlist makes isEmailAllowed() return true,
// so this hook is a no-op in `open` mode — `disableSignUp` and the
// posture above are what actually govern whether signup is permitted.
if (!user.email) {
throw new APIError('BAD_REQUEST', { message: 'Email is required.' });
}
if (!isEmailAllowed(user.email, allowedEmails)) {
throw new APIError('FORBIDDEN', {
message: 'This email is not on the invite allowlist.',
});
}
return { data: user };
},
},
},View on GitHub (pinned to 0773b97458)
Solutions
- If registration should be allowed, change the signup mode to 'open' (or 'allowlist') in the server config / SIGNUP_MODE env var and restart.
- If the instance is intentionally closed, direct the user to be invited or to use an existing account instead of signing up.
- Check for a version mismatch where the auth library's `disableSignUp` pre-gate is no longer blocking registration, making users hit this defensive hook; update or re-verify the getSignupMode wiring.
- If the user is already supposed to exist, have an admin create the user manually rather than registering.
Example fix
// before: server config SIGNUP_MODE=disabled // after: allow registration SIGNUP_MODE=open
Defensive patterns
Strategy: validation
Validate before calling
const res = await fetch('/api/get-signup-mode');
const { mode } = await res.json();
if (mode === 'disabled') {
// hide the signup UI / skip the signUp call entirely
return;
}
await authClient.signUp.email({ email, password, name }); Try / catch
try {
await authClient.signUp.email({ email, password, name });
} catch (e) {
if (e?.status === 403 && /signup is disabled/i.test(e?.message ?? '')) {
showNotice('Registration is closed on this instance.');
} else throw e;
} Prevention
- Query the signup-mode endpoint before rendering any signup form.
- For self-hosted instances, document SIGNUP_MODE in the deployment env file.
- Treat 403-with-'Signup is disabled.' as expected UX, not a bug.
When it happens
Trigger: A client calls the signup/registration endpoint (e.g. better-auth signUp.email) while the instance's signup mode is 'disabled' (SIGNUP_MODE=disabled or equivalent), so `signupDisabled` is true inside buildAuth's createUser hook.
Common situations: Self-hosted operators running a private instance where signups are closed; a user trying to register with 'Sign up with email' on an invite-only deployment; misconfigured SIGNUP_MODE left at 'disabled' from initial setup; the upstream `disableSignUp` flag failing to block the request, leaving this hook as the last line of defense.
Related errors
- This email is not on the invite allowlist.
- No chat in context
- Gitea API error: ${String(response.status)} ${response.statu
- Gitea API error: ${String(response.status)}
- Cannot access repository at ${repoPath}: ${err.code ?? err.m
AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01).
Data as JSON: /api/errors/d67179dc858f1353.
Report an issue: GitHub.