gotify/server · error
groups claim %q is missing
Error message
groups claim %q is missing
What it means
resolvePermission reads the configured groups claim (a.GroupsClaim) from the ID token or userinfo to compute admin/user permissions. If the claim is absent from both sources it returns HTTP 500 with 'groups claim %q is missing'. Without the groups claim the library cannot map the identity to local roles when GroupsClaim is configured.
Source
Thrown at api/oidc.go:560
return client, nil
}
func (a *OIDCAPI) popPendingSession(key string) (*pendingOIDCSession, bool) {
session, ok := a.pendingSessions.Pop(key)
if ok && time.Since(session.CreatedAt) < pendingSessionMaxAge {
return session, true
}
return nil, false
}
func (a *OIDCAPI) resolvePermission(idTokenClaims, userInfoClaims map[string]any) (bool, int, error) {
if a.GroupsClaim == "" {
return false, 0, nil
}
groupsRaw, ok := lookupClaim(a.GroupsClaim, idTokenClaims, userInfoClaims)
if !ok {
return false, http.StatusInternalServerError, fmt.Errorf("groups claim %q is missing", a.GroupsClaim)
}
var groups []string
switch groupsRaw := groupsRaw.(type) {
case []string:
groups = groupsRaw
case []any:
for _, groupRaw := range groupsRaw {
group, ok := groupRaw.(string)
if !ok {
return false, http.StatusInternalServerError, fmt.Errorf("groups claim %q contains a non-string element: %#v", a.GroupsClaim, groupRaw)
}
groups = append(groups, group)
}
case string:
groups = append(groups, groupsRaw)
default:
return false, http.StatusInternalServerError, fmt.Errorf("groups claim %q is not a string or string array: %#v", a.GroupsClaim, groupsRaw)View on GitHub (pinned to 14bfc25627)
Solutions
- Configure the IdP to include the groups claim (Keycloak protocol mapper / Azure group claims)
- Set GroupsClaim to the exact emitted claim name
- Add required scopes/client scopes so the claim is issued
- Verify by decoding a live token and checking the claim exists
Example fix
// before OIDC_GROUPS_CLAIM=memberOf // after (Keycloak default) OIDC_GROUPS_CLAIM=groups
Defensive patterns
Strategy: validation
Validate before calling
// validate at startup with a decoded sample token
var claims map[string]any
json.Unmarshal(sampleTokenPayload, &claims)
if _, ok := claims[groupsClaim]; !ok {
log.Fatalf("groups claim %q absent from IdP tokens; configure mapper or scopes", groupsClaim)
} Type guard
func hasGroupsClaim(claims map[string]any, name string) bool {
_, ok := claims[name]
return ok
} Try / catch
user, status, err := resolveUser(...)
if err != nil && strings.Contains(err.Error(), "groups claim") {
// IdP token misconfiguration: enable group mapper / scopes
http.Error(w, "role mapping misconfigured", http.StatusInternalServerError)
return
} Prevention
- Enable the groups protocol mapper (Keycloak) or group claims (Azure app registration)
- Keep GroupsClaim config in sync with the IdP after any rename
- Add required client scopes for groups
- Smoke-test tokens with jwt.io after IdP changes
When it happens
Trigger: GroupsClaim is set (e.g. 'groups') but neither idTokenClaims nor userInfoClaims contains it — mapper profile not enabled, client not assigned the groups mapper/protocol mapper, or scopes omit the claim.
Common situations: Keycloak: groups mapper not added to the client scope; Azure AD: group claims not configured in the app registration/token configuration; gitlab provider needing 'groups' scope; renaming the claim in the IdP without updating config.
Related errors
- username claim %q is missing
- a local user with the username %s already exists and linking
- groups claim %q contains a non-string element: %#v
- groups claim %q is not a string or string array: %#v
- issuer claim was empty
AI-assisted analysis of gotify/server@14bfc25627 (2026-09-05).
Data as JSON: /api/errors/024e45fa1fb0c87e.
Report an issue: GitHub.