{"record":{"id":"4638eaf17316fee8","repo":"siyuan-note/siyuan","slug":"save-oidc-login-session-failed","errorCode":null,"errorMessage":"Save OIDC login session failed","messagePattern":"Save OIDC login session failed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/oidc.go","lineNumber":948,"sourceCode":"\t\t\t\tret = append(ret, values[0])\n\t\t\t}\n\t\t}\n\t\treturn ret\n\tdefault:\n\t\treturn nil\n\t}\n}\n\nfunc authenticateOIDCSession(c *gin.Context, rememberMe bool) error {\n\tsession := util.GetSession(c)\n\tworkspaceSession := util.GetWorkspaceSession(session)\n\tworkspaceSession.AccessAuthCode = \"\"\n\tapplyAuthenticatedSession(c, workspaceSession, rememberMe)\n\tutil.WrongAuthCount = 0\n\tutil.AuthThrottleReset(c.ClientIP())\n\tif err := session.Save(c); err != nil {\n\t\tlogging.LogErrorf(\"save OIDC session failed: %s\", err)\n\t\treturn errors.New(\"Save OIDC login session failed\")\n\t}\n\tutil.BroadcastByType(\"auth\", \"loginAuth\", 0, \"\", nil)\n\treturn nil\n}\n\nfunc secureRandomToken(size int) (string, error) {\n\tbuffer := make([]byte, size)\n\tif _, err := rand.Read(buffer); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn base64.RawURLEncoding.EncodeToString(buffer), nil\n}\n\nfunc safeOIDCRedirectTarget(target string) string {\n\tparsed, err := url.Parse(target)\n\tif err != nil || parsed.IsAbs() || strings.HasPrefix(target, \"//\") || !strings.HasPrefix(target, \"/\") ||\n\t\tstrings.Contains(target, \"\\\\\") {\n\t\treturn \"/\"","sourceCodeStart":930,"sourceCodeEnd":966,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/oidc.go#L930-L966","documentation":"After successful OIDC authentication, authenticateOIDCSession writes the authenticated workspace session via the gin session store (cookie or backend). If session.Save fails — e.g. the response writer already committed, cookie serialization fails, or the store errors — the login cannot be persisted, so it returns this error after logging details.","triggerScenarios":"OIDCCallback, OIDCMobileCallback, OIDCPoll, or OIDCValidateActivate completes authentication but session.Save(c) returns an error — response already written, oversized session cookie exceeding the 4KB browser limit, secure-cookie encode failure due to bad secret, or a disconnected client.","commonSituations":"Cookie store secret misconfigured or rotated mid-session; session data too large for a cookie; calling the callback twice so headers are already sent; reverse proxy stripping Set-Cookie headers causing client-side churn (rare save failure); client closed connection before headers flushed.","solutions":["Check kernel logs for the preceding 'save OIDC session failed: %s' line to see the underlying store error","Reduce session payload size (avoid storing large values) so the auth cookie fits browser limits","Verify a stable session store secret is configured and shared across kernel restarts/replicas","Ensure the callback handler saves the session before writing any response body or headers","Test with a direct kernel connection (no proxy) to rule out Set-Cookie interference"],"exampleFix":"// before\nutil.BroadcastByType(\"auth\", \"loginAuth\", 0, \"\", nil)\nif err := session.Save(c); err != nil { ... } // headers already committed\n// after\nif err := session.Save(c); err != nil { ... return err }\nutil.BroadcastByType(\"auth\", \"loginAuth\", 0, \"\", nil)","handlingStrategy":"try-catch","validationCode":"// Go: save the session before any response write so headers are not committed\n// (structural check, not pre-callable)\nif c.Writer.Written() { return errors.New(\"response already committed; cannot save session\") }","typeGuard":null,"tryCatchPattern":"if err := model.OIDCCallback(c, code); err != nil && strings.Contains(err.Error(), \"Save OIDC login session failed\") {\n    logging.LogErrorf(\"oidc session save failed: %v\", err)\n    renderAuthError(c, \"Could not establish your session; please retry login\")\n}","preventionTips":["Always session.Save before writing the response body","Keep session payloads small so auth cookies stay under browser size limits","Use a stable, correctly configured session store secret across restarts"],"tags":["oidc","session","gin","cookie"],"backgroundTag":"session-save-failed","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}