{"record":{"id":"5c9775d784707175","repo":"semaphoreui/semaphore","slug":"failed-to-link-external-account","errorCode":null,"errorMessage":"Failed to link external account.","messagePattern":"Failed to link external account\\.","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"api/login.go","lineNumber":965,"sourceCode":"\t}\n\n\tif claims.sub == \"\" {\n\t\tlog.Error(fmt.Errorf(\"oidc provider %s returned no sub claim\", pid))\n\t\thttp.Error(w, \"OIDC sign-in failed: the provider returned no user ID (sub claim). Contact your administrator.\", http.StatusBadGateway)\n\t\treturn\n\t}\n\n\tif stateData.Link {\n\t\tsession, ok := getSession(r)\n\t\tif !ok || !session.IsVerified() {\n\t\t\thttp.Error(w, \"You must be signed in to link an external account.\", http.StatusUnauthorized)\n\t\t\treturn\n\t\t}\n\n\t\tsessionUser, uErr := helpers.Store(r).GetUser(session.UserID)\n\t\tif uErr != nil {\n\t\t\tlog.Error(uErr.Error())\n\t\t\thttp.Error(w, \"Failed to link external account.\", http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tif lErr := linkExternalIdentity(helpers.Store(r), sessionUser, db.IdentityTypeOidc, pid, claims.sub); lErr != nil {\n\t\t\tlog.WithError(lErr).WithFields(log.Fields{\n\t\t\t\t\"user_id\":  sessionUser.ID,\n\t\t\t\t\"provider\": pid,\n\t\t\t\t\"context\":  \"oidc_link\",\n\t\t\t}).Error(\"Failed to link external identity\")\n\n\t\t\tswitch {\n\t\t\tcase errors.Is(lErr, errIdentityLinkedToAnother):\n\t\t\t\thttp.Error(w, \"This external account is already linked to another user.\", http.StatusConflict)\n\t\t\tcase errors.Is(lErr, errProviderAlreadyLinked):\n\t\t\t\thttp.Error(w, \"Your account already has a linked identity for this provider. Unlink it first.\", http.StatusConflict)\n\t\t\tdefault:\n\t\t\t\thttp.Error(w, \"Failed to link external account.\", http.StatusInternalServerError)\n\t\t\t}","sourceCodeStart":947,"sourceCodeEnd":983,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/api/login.go#L947-L983","documentation":"During the link flow, oidcRedirect loads the logged-in user via helpers.Store(r).GetUser(session.UserID). If the store returns an error (user row missing, DB failure), it returns HTTP 500 'Failed to link external account.' The detailed cause goes to the log only.","triggerScenarios":"Session references a user that was deleted from the database while the session was still valid; database connectivity/query error in GetUser; replica/read inconsistency where the user record isn't visible.","commonSituations":"Admin deleted a user account that still had an active session; transient DB outage during linking; data migration left orphaned sessions pointing at removed user IDs.","solutions":["Check the server log for the underlying GetUser error","Verify the user in session still exists in the database (users table)","Clear the stale session (log out/in) so a deleted-user session is not reused","Check database connectivity and recent migrations for integrity issues"],"exampleFix":"-- before: session points at deleted user\nDELETE FROM sessions WHERE user_id NOT IN (SELECT id FROM users);\n-- after: sessions cleaned, user re-logs in and retries linking","handlingStrategy":"validation","validationCode":"if _, err := helpers.Store(r).GetUser(session.UserID); err != nil {\n    // invalidate stale session before proceeding\n    return errors.New(\"session user no longer exists\")\n}","typeGuard":null,"tryCatchPattern":"sessionUser, uErr := helpers.Store(r).GetUser(session.UserID)\nif uErr != nil {\n    log.Errorf(\"GetUser(%s) failed: %v\", session.UserID, uErr)\n    // clear session and force re-login\n    clearSession(w, r)\n    return\n}","preventionTips":["Invalidate sessions when deleting users","Monitor DB health for the store backend","Add a foreign-key/consistency check between sessions and users"],"tags":["database","http-500","session"],"backgroundTag":"record-not-found","analyzedSha":"1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa","analyzedAt":"2026-09-07T11:00:33.293Z","contentChangedAt":"2026-09-07T11:00:33.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}