{"record":{"id":"20ed67c613b1203d","repo":"gotify/server","slug":"err-error","errorCode":null,"errorMessage":"err.Error()","messagePattern":"err\\.Error\\(\\)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"api/oidc.go","lineNumber":228,"sourceCode":"//\t- name: state\n//\t  in: query\n//\t  description: the state parameter for CSRF protection\n//\t  required: true\n//\t  type: string\n//\tresponses:\n//\t  200:\n//\t    description: ok\n//\t  307:\n//\t    description: Redirect to UI\n//\t  default:\n//\t    description: Error\n//\t    schema:\n//\t        $ref: \"#/definitions/Error\"\nfunc (a *OIDCAPI) CallbackHandler() gin.HandlerFunc {\n\tcallback := func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens[*oidc.IDTokenClaims], state string, provider rp.RelyingParty, info *oidc.UserInfo) {\n\t\tuser, status, err := a.resolveUser(tokens.IDTokenClaims, info)\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), status)\n\t\t\treturn\n\t\t}\n\t\tsession, ok := a.popPendingSession(state)\n\t\tif !ok {\n\t\t\thttp.Error(w, \"unknown or expired state\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\n\t\tif session.Elevate != nil {\n\t\t\ta.handleElevationCallback(w, session.Elevate, user)\n\t\t\treturn\n\t\t}\n\n\t\tclient, err := a.createClient(session.ClientName, user.ID)\n\t\tif err != nil {\n\t\t\thttp.Error(w, fmt.Sprintf(\"failed to create client: %v\", err), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/gotify/server/blob/14bfc256276775c425f988d621dccfe705de18ac/api/oidc.go#L210-L246","documentation":"The OIDC CallbackHandler resolves the authenticated user from ID token claims and userinfo. On failure it writes the resolver's error message and its HTTP status directly to the response, so the client sees the raw resolveUser error text.","triggerScenarios":"resolveUser returns an error during the OIDC callback — e.g. required claims (username/email) missing from the token, or user provisioning/matching against the database failing.","commonSituations":"OIDC provider not configured to release the claims Gotify requires, email not verified, or database issues while creating/matching the user at first login.","solutions":["Read the response body's error text and status to identify which resolution step failed","Configure the OIDC provider to include the required claims (e.g. email, preferred_username) in the ID token","Check server logs for the underlying error from resolveUser","Verify the user database is reachable and not read-only"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"resp, err := http.Get(callbackURL)\nif resp.StatusCode >= 400 {\n    body, _ := io.ReadAll(resp.Body)\n    msg := string(body)\n    switch {\n    case strings.Contains(msg, \"claim\"):\n        log.Printf(\"OIDC provider claim config issue: %s\", msg)\n    case strings.Contains(msg, \"not found\"):\n        log.Printf(\"user provisioning failed: %s\", msg)\n    default:\n        log.Printf(\"callback error (%d): %s\", resp.StatusCode, msg)\n    }\n}","preventionTips":["Configure the OIDC provider to release all required claims","Test the full login flow with the real provider in staging","Log resolveUser failures server-side with context for debugging","Map provider errors to user-friendly messages client-side"],"tags":["oidc","authentication","callback"],"backgroundTag":"oidc-user-resolution-failed","analyzedSha":"14bfc256276775c425f988d621dccfe705de18ac","analyzedAt":"2026-09-05T12:52:36.781Z","contentChangedAt":"2026-09-05T12:52:36.781Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}