gotify/server · critical
failed to create user: %w
Error message
failed to create user: %w
What it means
registerUser wraps a failure from DB.CreateUser while auto-provisioning a new account for a first-time OIDC login as 'failed to create user: %w' with HTTP 500. The identity is valid and unmatched, but the INSERT into the user store failed, so registration is aborted.
Source
Thrown at api/oidc.go:519
return user, 0, nil
}
func (a *OIDCAPI) registerUser(username, oidcID string, hasAdminGroup bool) (*model.User, int, error) {
if !a.AutoRegister {
return nil, http.StatusForbidden, errors.New("user does not exist and auto-registration is disabled")
}
user := &model.User{
Name: username,
Pass: nil,
OIDCID: &oidcID,
}
if len(a.GroupsAdmin) > 0 {
user.Admin = hasAdminGroup
}
if err := a.DB.CreateUser(user); err != nil {
return nil, http.StatusInternalServerError, fmt.Errorf("failed to create user: %w", err)
}
log.Info().Str("oidc_id", oidcID).Str("username", user.Name).Bool("admin", user.Admin).Msg("OIDC auto registration")
if err := a.UserChangeNotifier.fireUserAdded(user.ID); err != nil {
log.Error().Err(err).Uint("user_id", user.ID).Msg("Could not notify user change")
}
return user, 0, nil
}
func (a *OIDCAPI) createClient(name string, userID uint) (*model.Client, error) {
elevatedUntil := time.Now().Add(model.DefaultElevationDuration)
tokenPublic, tokenPrivate := generateClientToken()
client := &model.Client{
Name: name,
Token: tokenPublic,
UserID: userID,
ElevatedUntil: &elevatedUntil,
ExpiresAfterInactivitySeconds: auth.CookieMaxAge,
}View on GitHub (pinned to 14bfc25627)
Solutions
- Check the wrapped driver error (duplicate key / constraint) in logs and address the specific constraint
- Run pending DB migrations for the users table
- Validate/sanitize the username claim length and characters at the IdP or in config
- Retry; if it was an insert race, the second login will link instead of register
Example fix
// before
username := fmt.Sprint(usernameRaw)
// after
username := fmt.Sprint(usernameRaw)
if len(username) > 64 { // keep within users.name column limit
username = username[:64]
} Defensive patterns
Strategy: try-catch
Validate before calling
// validate username length/charset before provisioning
if len(username) > 64 || strings.ContainsAny(username, "\x00") {
return errors.New("username from IdP violates user table constraints")
} Try / catch
user, status, err := resolveUser(...)
if err != nil && strings.Contains(err.Error(), "failed to create user") {
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) && pgErr.Code == "23505" {
// duplicate key race: retry login; it will link instead of register
}
http.Error(w, err.Error(), status)
return
} Prevention
- Run pending migrations before enabling auto-registration
- Cap/validate the username claim at the IdP
- Handle unique-key races by retrying the login
- Monitor CreateUser failures and DB disk/quota
When it happens
Trigger: First-ever OIDC login for an identity: no user by OIDC ID or username exists, registerUser builds a new model.User and a.DB.CreateUser(user) errors.
Common situations: Username from the claim violates DB constraints (too long, invalid chars, duplicate after race); users table out of migration; DB quota/disk full; unique index race between two simultaneous first logins.
Related errors
- failed to bind user to OIDC identity: %w
- failed to create client: %v
- database error: %v
- failed to elevate session: %v
- issuer claim was empty
AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05).
Data as JSON: /api/errors/1322537e4629468c.
Report an issue: GitHub.