Significant-Gravitas/AutoGPT · error · APIError
FORBIDDEN
FORBIDDEN
Error message
Signups are not allowed.
What it means
An APIError with code FORBIDDEN thrown from a Supabase databaseHooks.user.create.before hook in the frontend auth config (src/lib/auth/auth.ts). The signup gate (signup-gate.ts) checks isSignupAllowed(email, config) for EVERY new user row — email/password signup and first OAuth sign-in alike — and throws when disallowed, using decision.reason or this default. The phrasing is intentionally matched so the frontend isWaitlistError() maps it to the 'not allowed' modal.
Source
Thrown at autogpt_platform/frontend/src/lib/auth/auth.ts:81
}
export const auth = betterAuth({
baseURL,
secret: process.env.BETTER_AUTH_SECRET,
database: authDbPool,
telemetry: { enabled: false },
databaseHooks: {
user: {
create: {
// Env-driven signup gate (see signup-gate.ts). Fires for both
// email/password signup AND a first OAuth sign-in, since both create
// a user row. Existing users and the SQL data-migration bypass it.
// The thrown message is phrased so the frontend `isWaitlistError()`
// maps it to the existing "not allowed" modal.
before: async (user: { email: string }) => {
const decision = isSignupAllowed(user.email, readSignupGateConfig());
if (!decision.allowed) {
throw new APIError("FORBIDDEN", {
message: decision.reason ?? "Signups are not allowed.",
});
}
},
},
update: {
// updateUserByEmail (fired when a change-email link is confirmed)
// runs this hook post-commit; mirror the now-verified email onto the
// platform User row so notifications/Stripe track the confirmed
// identity. See email-mirror.ts for the why.
after: async (user: { id: string; email: string }) => {
await mirrorVerifiedEmailToPlatformUser(authDbPool, user);
},
},
},
},
advanced: {
database: {View on GitHub (pinned to 9c8bb5550f)
Solutions
- Check the signup-gate config (env vars read by readSignupGateConfig in signup-gate.ts) — disable the gate or add the email/domain to the allowlist.
- Restart the frontend after changing the env vars; the config is read server-side.
- If the user SHOULD be allowed, verify the exact email (case, aliases like +tags, OAuth provider email vs allowlisted one).
- For data migrations, insert users via SQL (documented bypass) instead of the auth API.
Example fix
# before (frontend/.env) SIGNUP_ALLOWLIST_ONLY=true # after — open signups SIGNUP_ALLOWLIST_ONLY=false
Defensive patterns
Strategy: validation
Validate before calling
import { isSignupAllowed, readSignupGateConfig } from "@/lib/auth/signup-gate";
function checkSignupAllowed(email: string): { allowed: boolean; reason?: string } {
return isSignupAllowed(email, readSignupGateConfig());
} Type guard
function isWaitlistError(err: unknown): boolean {
return (
err instanceof Error && /signups are not allowed/i.test(err.message)
);
} Try / catch
// server action / route handler wrapping supabase.auth.signUp
try {
await supabase.auth.signUp({ email, password });
} catch (error) {
if (isWaitlistError(error)) {
return { error: "Signups are currently restricted. Join the waitlist instead." };
}
throw error;
} Prevention
- Run isSignupAllowed(email, config) BEFORE calling signUp to give users a precise message instead of a thrown hook error.
- Keep the thrown message phrasing in lockstep with isWaitlistError() — the frontend maps on the exact wording.
- Document the gate env vars in deployment docs so operators know signups are closed by default in this mode.
When it happens
Trigger: A new user completing email/password signup or first OAuth sign-in while the signup gate config disallows it: waitlist mode enabled without the email on the allowlist, allowlist-only mode with an unknown email, or domain-restriction mode with a disallowed domain. Existing users never hit this (their rows already exist); SQL data-migration inserts bypass the hook.
Common situations: Self-hosters leaving waitlist/allowlist enabled after meaning to open signups; SignupRestrictedUntil / allowlist env vars set in .env (see signup-gate.ts config source) that the operator forgot; invited users signing in with a different email than the one allowlisted.
Related errors
- Authentication failed — please sign in again.
- Failed to update email
- Failed to update email
- Server returned ${status}.
- Authentication error — please sign in again.
AI-assisted analysis of Significant-Gravitas/AutoGPT@9c8bb5550f (2026-08-14).
Data as JSON: /api/errors/adeda1a3bc996811.
Report an issue: GitHub.