gotify/server · error

database error: %v

Error message

database error: %v

What it means

handleElevationCallback (api/oidc.go:260) returns HTTP 500 'database error: %v' when a.DB.GetClientByID(elevate.ClientID) returns a hard error while resolving the client referenced by the pending elevation session. Unlike the 404 case, this means the lookup itself failed (connection, query, or driver error), not that the client is missing.

Source

Thrown at api/oidc.go:260

		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
	}
	if client == nil || client.UserID != user.ID {
		http.Error(w, "client not found", http.StatusNotFound)
		return
	}
	elevatedUntil := time.Now().Add(time.Duration(elevate.DurationSeconds) * time.Second)
	if err := a.DB.UpdateClientElevatedUntil(client.ID, &elevatedUntil); err != nil {
		http.Error(w, fmt.Sprintf("failed to elevate session: %v", err), http.StatusInternalServerError)
		return
	}

	// The UI rechecks the authentication when the tab is closed.
	w.WriteHeader(http.StatusOK)
	w.Header().Add("content-type", "text/html")
	io.WriteString(w, `<!DOCTYPE html>
<html lang="en">
<head>

View on GitHub (pinned to 14bfc25627)

Solutions

  1. Read the wrapped driver error and check database connectivity/health from the gotify host
  2. Retry the elevation flow after the database recovers (a fresh state is required)
  3. Check for schema mismatches and run pending migrations
  4. Increase connection-pool/timeout settings if the error is a timeout under load
  5. Inspect DB server logs for the corresponding error at the same timestamp
Defensive patterns

Strategy: retry

Validate before calling

// Before driving users through the elevation flow, confirm the DB answers
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
    // defer or skip the elevation flow until connectivity is restored
    return
}

Try / catch

c, err := db.GetClientByID(id)
if err != nil {
    if isTransient(err) { // net.Error, driver.ErrBadConn, context deadline
        return retryWithBackoff(err)
    }
    return fmt.Errorf("get client by id: %w", err)
}

Prevention

When it happens

Trigger: The OIDC elevation flow (ElevateHandler -> provider -> CallbackHandler -> handleElevationCallback) reaches GetClientByID and the DB layer errors: connection refused/dropped, timeout, SQL syntax or schema error, or driver-level failure.

Common situations: Database restarted or unreachable mid-session; connection pool exhausted under load; schema drift after an upgrade; transient network partition between gotify and a remote Postgres.

Related errors


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