{"record":{"id":"ab08f730ea552b35","repo":"toeverything/AFFiNE","slug":"internal-server-error-ab08f7","errorCode":"internal_server_error","errorMessage":"An internal error occurred.","messagePattern":"An internal error occurred\\.","errorType":"exception","errorClass":"InternalServerError","httpStatus":500,"severity":"critical","filePath":"packages/backend/server/src/core/selfhost/controller.ts","lineNumber":59,"sourceCode":"    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    });\n\n    try {\n      await this.models.userFeature.add(\n        user.id,\n        'administrator',\n        'selfhost setup'\n      );\n\n      await this.sessionIssuer.issue(req, res, {\n        userId: user.id,\n        method: 'password',","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/core/selfhost/controller.ts#L41-L77","documentation":"Thrown by CustomSetupController.createAdmin when Mutex.acquire('createFirstAdmin') fails to obtain the cross-instance lock. This indicates another process or replica is currently creating the first admin (or the mutex backend — Redis/mutex table — is unreachable/misconfigured).","triggerScenarios":"Two concurrent POST /api/setup/create-admin-user requests race past the initialized() check; multi-replica self-host deployment where two pods run setup simultaneously; the configured mutex store (Redis) is down or misconfigured so acquire returns null.","commonSituations":"HA deployment without a shared mutex backend; user double-clicks the setup submit; init job retries before the first run completed; Redis connection string wrong in config.","solutions":["Retry the request once after a short delay — the first request likely completed initialization.","Verify the mutex backend (Redis or DB) is reachable from every server replica.","Add a single leader/init container responsibility so only one pod runs setup.","Confirm ServerService.initialized() flips true after success so subsequent calls short-circuit at the earlier ActionForbidden guard."],"exampleFix":"// before\nawait using lock = await this.mutex.acquire('createFirstAdmin');\nif (!lock) throw new InternalServerError();\n\n// after\nawait using lock = await this.mutex.acquire('createFirstAdmin');\nif (!lock) {\n  throw new InternalServerError('Setup lock unavailable — another instance may be initializing. Retry in a few seconds.');\n}","handlingStrategy":"retry","validationCode":"const initialized = await server.initialized();\nif (initialized) return; // nothing to do\n// only attempt setup if no other replica is initializing\nconst lock = await mutex.tryAcquire('createFirstAdmin');\nif (!lock) return retryLater();","typeGuard":"// n/a","tryCatchPattern":"try {\n  await createAdmin(input);\n} catch (e) {\n  if (e?.code === 'internal_server_error') {\n    // back off and retry once; the mutex may free or setup may complete\n    await sleep(2000);\n    return createAdmin(input);\n  }\n  throw e;\n}","preventionTips":["Run setup from a single leader/init container in HA deployments.","Verify the mutex backend (Redis) is reachable before invoking setup.","Treat initialized()==true as terminal and skip the call entirely."],"tags":["selfhost","concurrency","mutex","nestjs","infrastructure"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}