{"record":{"id":"1179b87f97da249d","repo":"gotify/server","slug":"failed-to-bind-user-to-oidc-identity-w","errorCode":null,"errorMessage":"failed to bind user to OIDC identity: %w","messagePattern":"failed to bind user to OIDC identity: %w","errorType":"http","errorClass":null,"httpStatus":500,"severity":"critical","filePath":"api/oidc.go","lineNumber":498,"sourceCode":"\t}\n\treturn a.registerUser(username, oidcID, hasAdminGroup)\n}\n\nfunc (a *OIDCAPI) linkExistingUser(user *model.User, oidcID string, hasAdminGroup bool) (*model.User, int, error) {\n\tif !a.LinkByUsername {\n\t\tlog.Warn().Str(\"oidc_id\", oidcID).Str(\"username\", user.Name).Msgf(\"OIDC login rejected: a local user with the username already exists and %s is disabled\", config.EnvOIDCLinkByUsername)\n\t\treturn nil, http.StatusForbidden, fmt.Errorf(\"a local user with the username %s already exists and linking by username is disabled\", user.Name)\n\t}\n\tif user.OIDCID != nil {\n\t\tlog.Warn().Str(\"oidc_id\", oidcID).Str(\"bound_oidc_id\", *user.OIDCID).Str(\"username\", user.Name).Msg(\"OIDC login rejected: the username is already bound to a different OIDC identity\")\n\t\treturn nil, http.StatusForbidden, fmt.Errorf(\"the user %s is already bound to a different OIDC identity\", user.Name)\n\t}\n\tuser.OIDCID = &oidcID\n\tif len(a.GroupsAdmin) > 0 {\n\t\tuser.Admin = hasAdminGroup\n\t}\n\tif err := a.DB.UpdateUser(user); err != nil {\n\t\treturn nil, http.StatusInternalServerError, fmt.Errorf(\"failed to bind user to OIDC identity: %w\", err)\n\t}\n\tlog.Warn().Str(\"oidc_id\", oidcID).Str(\"username\", user.Name).Bool(\"admin\", user.Admin).Msg(\"OIDC link by username\")\n\treturn user, 0, nil\n}\n\nfunc (a *OIDCAPI) registerUser(username, oidcID string, hasAdminGroup bool) (*model.User, int, error) {\n\tif !a.AutoRegister {\n\t\treturn nil, http.StatusForbidden, errors.New(\"user does not exist and auto-registration is disabled\")\n\t}\n\tuser := &model.User{\n\t\tName:   username,\n\t\tPass:   nil,\n\t\tOIDCID: &oidcID,\n\t}\n\n\tif len(a.GroupsAdmin) > 0 {\n\t\tuser.Admin = hasAdminGroup\n\t}","sourceCodeStart":480,"sourceCodeEnd":516,"githubUrl":"https://github.com/gotify/server/blob/14bfc256276775c425f988d621dccfe705de18ac/api/oidc.go#L480-L516","documentation":"linkExistingUser wraps a failure from DB.UpdateUser while persisting the newly bound OIDCID (and possibly the Admin flag) as 'failed to bind user to OIDC identity: %w' with HTTP 500. Authentication and lookup succeeded, but the binding write failed so the link is not established.","triggerScenarios":"user.OIDCID = &oidcID and optional Admin update are set, then a.DB.UpdateUser(user) returns an error during a link-by-username login.","commonSituations":"DB connectivity loss; optimistic-lock/concurrent-update conflicts when the same user logs in from two sessions; NOT NULL/length constraints on oidc_id column; schema not migrated for the OIDCID column.","solutions":["Read the wrapped driver error in logs and fix the underlying storage issue","Verify the users table schema supports the oidc_id column (run migrations)","Retry the login; the next attempt will re-run the link","Avoid concurrent first logins for the same account during migration"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// ensure schema supports oidc_id before enabling OIDC\nvar count int\n_ = db.QueryRow(\"SELECT COUNT(*) FROM information_schema.columns WHERE table_name='users' AND column_name='oidc_id'\").Scan(&count)\nif count == 0 { return errors.New(\"run migrations: users.oidc_id missing\") }","typeGuard":null,"tryCatchPattern":"user, status, err := resolveUser(...)\nif err != nil && strings.Contains(err.Error(), \"failed to bind user to OIDC identity\") {\n    log.Error().Err(err).Msg(\"OIDC bind failed; check DB\")\n    http.Error(w, \"temporary server error\", http.StatusInternalServerError)\n    return\n}","preventionTips":["Run migrations on every deploy","Avoid concurrent first logins during migrations","Add uniqueness/index constraints matching UpdateUser expectations","Alert on UpdateUser failures"],"tags":["database","oidc","http-500"],"backgroundTag":"database-update-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"}