gotify/server · critical

failed to bind user to OIDC identity: %w

Error message

failed to bind user to OIDC identity: %w

What it means

linkExistingUser wraps a failure from DB.UpdateUser while persisting the newly bound OIDCID (and possibly the Admin flag) as 'failed to bind user to OIDC identity: %w' with HTTP 500. Authentication and lookup succeeded, but the binding write failed so the link is not established.

Source

Thrown at api/oidc.go:498

	}
	return a.registerUser(username, oidcID, hasAdminGroup)
}

func (a *OIDCAPI) linkExistingUser(user *model.User, oidcID string, hasAdminGroup bool) (*model.User, int, error) {
	if !a.LinkByUsername {
		log.Warn().Str("oidc_id", oidcID).Str("username", user.Name).Msgf("OIDC login rejected: a local user with the username already exists and %s is disabled", config.EnvOIDCLinkByUsername)
		return nil, http.StatusForbidden, fmt.Errorf("a local user with the username %s already exists and linking by username is disabled", user.Name)
	}
	if user.OIDCID != nil {
		log.Warn().Str("oidc_id", oidcID).Str("bound_oidc_id", *user.OIDCID).Str("username", user.Name).Msg("OIDC login rejected: the username is already bound to a different OIDC identity")
		return nil, http.StatusForbidden, fmt.Errorf("the user %s is already bound to a different OIDC identity", user.Name)
	}
	user.OIDCID = &oidcID
	if len(a.GroupsAdmin) > 0 {
		user.Admin = hasAdminGroup
	}
	if err := a.DB.UpdateUser(user); err != nil {
		return nil, http.StatusInternalServerError, fmt.Errorf("failed to bind user to OIDC identity: %w", err)
	}
	log.Warn().Str("oidc_id", oidcID).Str("username", user.Name).Bool("admin", user.Admin).Msg("OIDC link by username")
	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
	}

View on GitHub (pinned to 14bfc25627)

Solutions

  1. Read the wrapped driver error in logs and fix the underlying storage issue
  2. Verify the users table schema supports the oidc_id column (run migrations)
  3. Retry the login; the next attempt will re-run the link
  4. Avoid concurrent first logins for the same account during migration
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure schema supports oidc_id before enabling OIDC
var count int
_ = db.QueryRow("SELECT COUNT(*) FROM information_schema.columns WHERE table_name='users' AND column_name='oidc_id'").Scan(&count)
if count == 0 { return errors.New("run migrations: users.oidc_id missing") }

Try / catch

user, status, err := resolveUser(...)
if err != nil && strings.Contains(err.Error(), "failed to bind user to OIDC identity") {
    log.Error().Err(err).Msg("OIDC bind failed; check DB")
    http.Error(w, "temporary server error", http.StatusInternalServerError)
    return
}

Prevention

When it happens

Trigger: user.OIDCID = &oidcID and optional Admin update are set, then a.DB.UpdateUser(user) returns an error during a link-by-username login.

Common situations: DB connectivity loss; optimistic-lock/concurrent-update conflicts when the same user logs in from two sessions; NOT NULL/length constraints on oidc_id column; schema not migrated for the OIDCID column.

Related errors


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