gotify/server · error

failed to create client: %v

Error message

failed to create client: %v

What it means

After a successful OIDC callback, api/oidc.go:244 wraps any error from a.createClient(session.ClientName, user.ID) into an HTTP 500 'failed to create client: %v'. createClient persists a new client record (with token) for the authenticated user; failure means the client could not be stored even though OIDC authentication itself succeeded.

Source

Thrown at api/oidc.go:244

		user, status, err := a.resolveUser(tokens.IDTokenClaims, info)
		if err != nil {
			http.Error(w, err.Error(), status)
			return
		}
		session, ok := a.popPendingSession(state)
		if !ok {
			http.Error(w, "unknown or expired state", http.StatusBadRequest)
			return
		}

		if session.Elevate != nil {
			a.handleElevationCallback(w, session.Elevate, user)
			return
		}

		client, err := a.createClient(session.ClientName, user.ID)
		if err != nil {
			http.Error(w, fmt.Sprintf("failed to create client: %v", err), http.StatusInternalServerError)
			return
		}
		auth.SetCookie(w, client.Token, auth.CookieMaxAge, a.SecureCookie)
		// A reverse proxy may have already stripped a url prefix from the URL
		// without us knowing, we have to make a relative redirect.
		// We cannot use http.Redirect as this normalizes the Path with r.URL.
		w.Header().Set("Location", "../../")
		w.WriteHeader(http.StatusTemporaryRedirect)
	}
	return gin.WrapF(rp.CodeExchangeHandler(rp.UserinfoCallback(callback), a.Provider))
}

func (a *OIDCAPI) handleElevationCallback(w http.ResponseWriter, elevate *pendingElevation, user *model.User) {
	client, err := a.DB.GetClientByID(elevate.ClientID)
	if err != nil {
		http.Error(w, fmt.Sprintf("database error: %v", err), http.StatusInternalServerError)
		return
	}

View on GitHub (pinned to 14bfc25627)

Solutions

  1. Check the wrapped inner error in the response/log and verify database connectivity and health
  2. Confirm database migrations are up to date for the gotify version
  3. Retry the OIDC login once the database is healthy; the state was consumed, so start a new login
  4. If a duplicate-name constraint is the cause, retry with a different client name
  5. Inspect DB logs for lock/permission/disk errors during the insert
Defensive patterns

Strategy: retry

Validate before calling

// Before initiating the login that will create a client, verify the DB is reachable
if err := db.Ping(); err != nil {
    return fmt.Errorf("database unavailable, fix before login: %w", err)
}

Try / catch

client, err := createClient(name, userID)
if err != nil {
    log.Printf("client creation failed: %v", err) // keep the wrapped cause
    if errors.Is(err, sql.ErrNoRows) || isConstraintViolation(err) {
        return retryWithNewName(name) // duplicate/conflict case
    }
    return retryWithBackoff(err) // transient DB error
}

Prevention

When it happens

Trigger: createClient fails while handling the provider callback: database unavailable or returning an error on insert, unique/name constraint violated by the requested client name, DB schema mismatch after an upgrade, or disk/storage errors during the insert.

Common situations: Postgres/SQLite down or connection pool exhausted during login; duplicate client name from retrying the login with the same name; migration not applied so the clients table lacks a column; database read-only or out of disk space.

Related errors


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