gotify/server · error

username already exists

Error message

username already exists

What it means

Returned by CreateUser with HTTP 400 when a user with the requested username already exists in the database (existingUser != nil in the create branch). The API treats usernames as unique and refuses to create a duplicate, unlike the update path which renames safely.

Source

Thrown at api/user.go:245

				return
			}
			if internal.Admin {
				ctx.AbortWithError(status, errors.New("you are not allowed to create an admin user"))
				return
			}
		}

		if existingUser == nil {
			if success := successOrAbort(ctx, 500, a.DB.CreateUser(internal)); !success {
				return
			}
			if err := a.UserChangeNotifier.fireUserAdded(internal.ID); err != nil {
				ctx.AbortWithError(500, err)
				return
			}
			ctx.JSON(200, toExternalUser(internal))
		} else {
			ctx.AbortWithError(400, errors.New("username already exists"))
		}
	}
}

// GetUserByID returns the user by id
// swagger:operation GET /user/{id} user getUser
//
// Get a user.
//
// Requires elevated authentication.
//
//	---
//	consumes: [application/json]
//	produces: [application/json]
//	security: [clientTokenAuthorizationHeader: [], clientTokenHeader: [], clientTokenQuery: [], basicAuth: []]
//	parameters:
//	- name: id
//	  in: path

View on GitHub (pinned to 14bfc25627)

Solutions

  1. Pick a different username, or delete/rename the existing user first
  2. Check existence before creating (GET the user by name/id) and update instead of create
  3. Make retry logic idempotent: on 400 'username already exists', treat prior create as succeeded
  4. Ensure test/setup scripts clean up created users

Example fix

// before
await api.createUser({ username: 'bob' }); // 400 if bob exists
// after
const existing = await api.getUserByName('bob').catch(() => null);
if (!existing) await api.createUser({ username: 'bob' });
Defensive patterns

Strategy: validation

Validate before calling

const existing = await findUserByName(username); // admin lookup or list+filter
if (existing) {
  throw new Error(`username "${username}" already exists`);
}

Type guard

function isUsernameFree(users, name) {
  return !users.some(u => u.username === name);
}

Try / catch

try {
  await api.createUser({ username, ...rest });
} catch (e) {
  if (e.status === 400 && /username already exists/.test(e.message)) { promptForDifferentUsername(); }
  else { throw e; }
}

Prevention

When it happens

Trigger: POST /api/users with a username that collides with an existing user (case as stored); retrying a creation that actually succeeded earlier; registration form submitted twice; LDAP/sync jobs re-creating users that already exist.

Common situations: Idempotency-unaware retry logic after a network timeout; test fixtures not cleaning up users between runs; users provisioned by two systems (manual + SSO sync) racing; case-sensitivity mismatch between the client and the DB collation.

Related errors


AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05). Data as JSON: /api/errors/4e85076b5ef40fb8. Report an issue: GitHub.