calcom/cal.diy · info · HttpError

No setup needed.

Error message

No setup needed.

What it means

Thrown by the /api/auth/setup route handler (HttpError, HTTP 400) when prisma.user.count() is non-zero, meaning the instance already has at least one user and first-run setup is therefore closed. It is a hard gate before any input is parsed.

Source

Thrown at apps/web/app/api/auth/setup/route.ts:31

import { IdentityProvider } from "@calcom/prisma/enums";
import { CreationSource } from "@calcom/prisma/enums";

const querySchema = z.object({
  username: z
    .string()
    .refine((val) => val.trim().length >= 1, { message: "Please enter at least one character" }),
  full_name: z.string().min(3, "Please enter at least 3 characters"),
  email_address: z.string().regex(emailRegex, { message: "Please enter a valid email" }),
  password: z.string().refine((val) => isPasswordValid(val.trim(), false, true), {
    message:
      "The password must be a minimum of 15 characters long containing at least one number and have a mixture of uppercase and lowercase letters",
  }),
});

async function handler(req: NextRequest) {
  const userCount = await prisma.user.count();
  if (userCount !== 0) {
    throw new HttpError({ statusCode: 400, message: "No setup needed." });
  }
  const body = await parseRequestData(req);

  const parsedQuery = querySchema.safeParse(body);
  if (!parsedQuery.success) {
    throw new HttpError({ statusCode: 422, message: parsedQuery.error.message });
  }

  const username = slugify(parsedQuery.data.username.trim());
  const userEmail = parsedQuery.data.email_address.toLowerCase();

  const hashedPassword = await hashPassword(parsedQuery.data.password);

  await prisma.user.create({
    data: {
      username,
      email: userEmail,
      password: { create: { hash: hashedPassword } },

View on GitHub (pinned to 176037d0af)

Solutions

  1. Skip the setup call when the instance is already initialized (treat this 400 as 'success: already done').
  2. In provisioning scripts, check GET /api/auth/setup or a health endpoint before attempting creation.
  3. If re-seeding is truly required, truncate users in the target DB first (non-production only).

Example fix

// before
await fetch('/api/auth/setup', { method: 'POST', body: payload });

// after
const res = await fetch('/api/auth/setup', { method: 'POST', body: payload });
if (res.status === 400 && (await res.json()).message === 'No setup needed.') {
  // instance already initialized; nothing to do
  return;
}
Defensive patterns

Strategy: validation

Validate before calling

// Provisioning helper: only attempt setup if no users exist yet
const status = await fetch('/api/auth/setup-status').then(r => r.json());
if (status.userCount > 0) {
  console.log('Instance already initialized; skipping setup.');
  return;
}
await fetch('/api/auth/setup', { method: 'POST', body: JSON.stringify(adminPayload) });

Try / catch

try {
  await fetch('/api/auth/setup', { method: 'POST', body });
} catch (e) {
  if (e instanceof HttpError && e.statusCode === 400 && /No setup needed/.test(e.message)) {
    return; // already initialized
  }
  throw e;
}

Prevention

When it happens

Trigger: POST /api/auth/setup on an already-initialized instance; an automated deploy script that always hits setup; retrying setup after a partial first run that already created the admin user.

Common situations: CI/CD running the setup step unconditionally, re-running setup after a failed-but-committed first user, environment restored from a DB snapshot that already contains users.

Related errors


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