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

  1. If registration should be allowed, change the signup mode to 'open' (or 'allowlist') in the server config / SIGNUP_MODE env var and restart.
  2. If the instance is intentionally closed, direct the user to be invited or to use an existing account instead of signing up.
  3. 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.
  4. 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

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


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/d67179dc858f1353. Report an issue: GitHub.