{"record":{"id":"aac2de6eebc483a3","repo":"gotify/server","slug":"you-are-not-allowed-to-create-an-admin-user","errorCode":null,"errorMessage":"you are not allowed to create an admin user","messagePattern":"you are not allowed to create an admin user","errorType":"http","errorClass":null,"httpStatus":403,"severity":"error","filePath":"api/user.go","lineNumber":230,"sourceCode":"\t\tif uid != nil {\n\t\t\trequestedBy, err = a.DB.GetUserByID(*uid)\n\t\t\tif err != nil {\n\t\t\t\tctx.AbortWithError(http.StatusInternalServerError, fmt.Errorf(\"could not get user: %s\", err))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif requestedBy == nil || !requestedBy.Admin {\n\t\t\tstatus := http.StatusUnauthorized\n\t\t\tif requestedBy != nil {\n\t\t\t\tstatus = http.StatusForbidden\n\t\t\t}\n\t\t\tif !a.Registration {\n\t\t\t\tctx.AbortWithError(status, errors.New(\"you are not allowed to access this api\"))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif internal.Admin {\n\t\t\t\tctx.AbortWithError(status, errors.New(\"you are not allowed to create an admin user\"))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif existingUser == nil {\n\t\t\tif success := successOrAbort(ctx, 500, a.DB.CreateUser(internal)); !success {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err := a.UserChangeNotifier.fireUserAdded(internal.ID); err != nil {\n\t\t\t\tctx.AbortWithError(500, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tctx.JSON(200, toExternalUser(internal))\n\t\t} else {\n\t\t\tctx.AbortWithError(400, errors.New(\"username already exists\"))\n\t\t}\n\t}\n}","sourceCodeStart":212,"sourceCodeEnd":248,"githubUrl":"https://github.com/gotify/server/blob/14bfc256276775c425f988d621dccfe705de18ac/api/user.go#L212-L248","documentation":"Returned by CreateUser when a caller without global admin rights attempts to create a user with the admin flag set. Even when self-registration (a.Registration) is enabled, registration may only ever create non-admin accounts; internal.Admin triggers a 401/403 abort matching the requester's auth status.","triggerScenarios":"POST /api/users with {\"admin\": true} in the body while authenticated as a non-admin (or anonymously with registration on); automation that copies an admin user object as a template and re-submits it; scripts attempting privilege escalation through the register endpoint.","commonSituations":"Onboarding tooling that always sends admin:true; API clients written when registration accepted any payload now blocked after a security fix; attempts to bootstrap a first admin through the public API instead of a CLI/init step.","solutions":["Use an admin account to create admin users","Remove \"admin\": true from the request body for self-registration flows","Create the first admin via the server's CLI/bootstrap mechanism, not the public API","Create a normal user first, then elevate with an admin UpdateUser call"],"exampleFix":"// before\ncurl -X POST https://host/api/users -d '{\"username\":\"bob\",\"admin\":true}'\n// after\ncurl -X POST https://host/api/users -u 'admin:s3cret' -d '{\"username\":\"bob\",\"admin\":true}'","handlingStrategy":"validation","validationCode":"if (payload.admin === true && !isAdminAccount(currentUser)) {\n  throw new Error('only admins may create admin users; drop the admin flag or use admin credentials');\n}","typeGuard":"function isAdminCreationRequest(payload, requester) {\n  return payload.admin === true && !(requester && requester.admin === true);\n}","tryCatchPattern":"try {\n  await api.createUser(payload);\n} catch (e) {\n  if (e.status === 403 && /admin user/.test(e.message)) { retry as non-admin or with admin creds }\n  else { throw e; }\n}","preventionTips":["Never include admin:true in self-registration payloads","Bootstrap initial admins via CLI/init, not the public API","Template user objects carefully; strip privilege fields before resubmitting","Elevate via a second admin-authenticated update instead of create-time flags"],"tags":["http","authorization","privilege-escalation","admin"],"backgroundTag":"insufficient-permissions","analyzedSha":"14bfc256276775c425f988d621dccfe705de18ac","analyzedAt":"2026-09-05T12:52:36.781Z","contentChangedAt":"2026-09-05T12:52:36.781Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}