{"record":{"id":"5cee75a5e36e807d","repo":"toeverything/AFFiNE","slug":"password-required","errorCode":"password_required","errorMessage":"Password is required.","messagePattern":"Password is required\\.","errorType":"exception","errorClass":"PasswordRequired","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/core/selfhost/controller.ts","lineNumber":48,"sourceCode":"    private readonly mutex: Mutex,\n    private readonly server: ServerService\n  ) {}\n\n  @Public()\n  @Post('/create-admin-user')\n  async createAdmin(\n    @Req() req: Request,\n    @Res() res: Response,\n    @Body() input: CreateUserInput\n  ) {\n    if (await this.server.initialized()) {\n      throw new ActionForbidden('First user already created');\n    }\n\n    validators.assertValidEmail(input.email);\n\n    if (!input.password) {\n      throw new PasswordRequired();\n    }\n\n    validators.assertValidPassword(\n      input.password,\n      this.config.auth.passwordRequirements\n    );\n\n    await using lock = await this.mutex.acquire('createFirstAdmin');\n\n    if (!lock) {\n      throw new InternalServerError();\n    }\n    const user = await this.models.user.create({\n      name: input.name || undefined,\n      email: input.email,\n      password: input.password,\n      registered: true,\n    });","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/selfhost/controller.ts#L30-L66","documentation":"Thrown by CustomSetupController.createAdmin when input.password is falsy during first-admin creation. It runs after the server-initialized check and before password-complexity validation, so an empty password never reaches assertValidPassword.","triggerScenarios":"POST /api/setup/create-admin-user with password omitted, empty string, or null in the JSON body; form submission where the password field was left blank.","commonSituations":"Setup form submitted before the password field was filled; client-side validation disabled; curl/script that omitted the password key; JSON payload typo (e.g. 'passwd' instead of 'password').","solutions":["Ensure the request body includes a non-empty 'password' field matching CreateUserInput.","Add client-side required-field validation before submitting the setup form.","Confirm the Content-Type is application/json so the body parses correctly into input.password."],"exampleFix":"// before\nconst body = { email };\nawait fetch('/api/setup/create-admin-user', { method: 'POST', body: JSON.stringify(body) });\n\n// after\nif (!password) throw new Error('password required');\nconst body = { email, password };\nawait fetch('/api/setup/create-admin-user', {\n  method: 'POST',\n  headers: { 'content-type': 'application/json' },\n  body: JSON.stringify(body),\n});","handlingStrategy":"validation","validationCode":"if (!input.password || typeof input.password !== 'string') {\n  return res.status(400).json({ code: 'password_required' });\n}","typeGuard":"function hasPassword(body: unknown): body is { password: string } {\n  return typeof (body as any)?.password === 'string' && (body as any).password.length > 0;\n}","tryCatchPattern":"try {\n  await createAdmin({ email, password });\n} catch (e) {\n  if (e?.code === 'password_required') {\n    // mark the password field as invalid in the setup form\n    return setFieldError('password', 'Password is required');\n  }\n  throw e;\n}","preventionTips":["Mark the password input as required and disable submit until non-empty.","Send Content-Type: application/json so the body parses into the DTO.","Cross-check field names against CreateUserInput in integration tests."],"tags":["selfhost","setup","validation","nestjs"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}