calcom/cal.diy · warning · HttpError

Signup is disabled

Error message

Signup is disabled

What it means

Thrown by the /api/auth/signup route (HttpError, HTTP 403) in ensureEnvironmentForSignup when either NEXT_PUBLIC_DISABLE_SIGNUP env is 'true' or the FeaturesRepository reports the 'disable-signup' feature flag is globally enabled. Team-invite signups (valid token) bypass this gate entirely.

Source

Thrown at apps/web/app/api/auth/signup/route.ts:32

import { checkCfTurnstileToken } from "@calcom/lib/server/checkCfTurnstileToken";
import { prisma } from "@calcom/prisma";
import { signupSchema } from "@calcom/prisma/zod-utils";

async function ensureSignupIsEnabled(body: Record<string, string>) {
  const { token } = signupSchema
    .pick({
      token: true,
    })
    .parse(body);

  // Still allow signups if there is a team invite
  if (token) return;

  const featuresRepository = new FeaturesRepository(prisma);
  const signupDisabled = await featuresRepository.checkIfFeatureIsEnabledGlobally("disable-signup");

  if (process.env.NEXT_PUBLIC_DISABLE_SIGNUP === "true" || signupDisabled) {
    throw new HttpError({
      statusCode: 403,
      message: "Signup is disabled",
    });
  }
}

async function handler(req: NextRequest) {
  const remoteIp = getIP(req);
  // Use a try catch instead of returning res every time
  try {
    // Rate limit: 10 signups per 60 seconds per IP
    await checkRateLimitAndThrowError({
      rateLimitingType: "core",
      identifier: `api:signup:${piiHasher.hash(remoteIp)}`,
    });

    const body = await parseRequestData(req);
    const query = Object.fromEntries(req.nextUrl.searchParams.entries());

View on GitHub (pinned to 176037d0af)

Solutions

  1. If public signup should be on, unset NEXT_PUBLIC_DISABLE_SIGNUP (or set to 'false') and disable the 'disable-signup' feature flag.
  2. If signup is intentionally disabled, accept invite-only flow: always sign up with a valid team-invite token so ensureEnvironmentForSignup returns early.
  3. Surface a clear 'registration is closed' message on 403 instead of retrying.

Example fix

// before
await signup({ username, email }); // fails 403 when disabled

// after
if (!inviteToken) {
  showNotice('Public registration is disabled. Use your team invite link.');
  return;
}
await signup({ username, email, token: inviteToken });
Defensive patterns

Strategy: validation

Validate before calling

// Detect disabled signup before attempting registration
if (!inviteToken && (process.env.NEXT_PUBLIC_DISABLE_SIGNUP === 'true')) {
  showNotice('Public registration is disabled. Use your team invite link.');
  return;
}
await signup({ username, email, token: inviteToken });

Try / catch

try {
  await signup(payload);
} catch (e) {
  if (e instanceof HttpError && e.statusCode === 403 && /Signup is disabled/.test(e.message)) {
    showRegistrationClosedNotice();
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: POST /api/auth/signup without a team-invite token while public signups are turned off via env var or feature flag.

Common situations: Self-hosted/enterprise deployment that disables public registration, a feature flag flipped in production, an env var misconfigured to 'true' unintentionally.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/f1d64c88a210b74f. Report an issue: GitHub.