gotify/server · error
client not found
Error message
client not found
What it means
handleElevationCallback (api/oidc.go:264) returns HTTP 404 'client not found' when GetClientByID returns no client for elevate.ClientID, or the client exists but belongs to a different user (client.UserID != user.ID). This is a deliberate ownership/authorization guard, not a database failure.
Source
Thrown at api/oidc.go:264
}
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>
<title>Gotify Session Elevation</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
</head>View on GitHub (pinned to 14bfc25627)
Solutions
- Verify the client ID still exists and is owned by the logged-in user, then restart the elevation flow from the UI
- If the client was deleted, create it again and elevate the new client
- Do not reuse or share elevation URLs; each is bound to one user and one client
- Check that the same database is being used across restarts/replicas
- Confirm the elevation was initiated for the account you actually authenticated with at the provider
Defensive patterns
Strategy: validation
Validate before calling
// Before starting the elevation flow, confirm the client exists and is yours
client, err := db.GetClientByID(clientID)
if err != nil {
return err // DB problem, different failure mode
}
if client == nil || client.UserID != currentUser.ID {
// do not start the elevation; it is guaranteed to 404 at the callback
return errors.New("client missing or not owned by current user")
} Try / catch
const res = await fetch(elevateCallbackURL);
if (res.status === 404 && (await res.text()).includes('client not found')) {
// client was deleted or belongs to another user: refresh client list,
// re-create if needed, then start a NEW elevation flow
await refreshClients();
return startElevation();
} Prevention
- Verify the client still exists right before initiating elevation
- Never share or replay elevation URLs across users or sessions
- Refresh the client list after deletions so the UI never elevates a stale ID
- Ensure all instances point at the same database
- Log the ClientID and UserID on this 404 to diagnose ownership mismatches
When it happens
Trigger: During the OIDC elevation callback: (1) the client was deleted between starting the elevation (ElevateHandler) and completing the provider round-trip; (2) the elevation state references a client owned by another user, e.g. after the state was crafted/replayed for a different account; (3) a stale elevation request referencing an ID from a different database/environment; (4) wrong ClientID stored in the pending elevation.
Common situations: User deletes the client (or an admin purges it) while the elevation tab is open at the provider; replaying an old elevation URL after re-authenticating as a different user; pointing gotify at a restored/migrated database missing that client row.
Related errors
- client not found
- user is not in any allowed group
- unknown plugin
- a local user with the username %s already exists and linking
- the user %s is already bound to a different OIDC identity
AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05).
Data as JSON: /api/errors/cad68cc552113106.
Report an issue: GitHub.