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
- Skip the setup call when the instance is already initialized (treat this 400 as 'success: already done').
- In provisioning scripts, check GET /api/auth/setup or a health endpoint before attempting creation.
- 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
- Make setup scripts idempotent and treat 'No setup needed' as success.
- Gate CI setup steps on a user-count precondition.
- Avoid re-running setup after the first user is committed.
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
- parsedQuery.error.message
- GetOrgId decorator: Organization ID not found
- PermissionsGuard - no authentication provided. Provide eithe
- PermissionsGuard - oAuth client with id=${oAuthClient.id} do
- PermissionsGuard - no oAuth client found for access token=${
AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12).
Data as JSON: /api/errors/44adead5331c0466.
Report an issue: GitHub.