{"record":{"id":"18ca14bbfe9cf404","repo":"siyuan-note/siyuan","slug":"too-many-pending-oidc-login-transactions","errorCode":null,"errorMessage":"too many pending OIDC login transactions","messagePattern":"too many pending OIDC login transactions","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/oidc.go","lineNumber":670,"sourceCode":"\t\ttransaction.Done = make(chan struct{})\n\t}\n\tif len(oidcTransactions.byState) >= oidcTransactionMax {\n\t\treturn errors.New(\"OIDC login transaction capacity reached\")\n\t}\n\tperIP, perBinding := 0, 0\n\tfor _, candidate := range oidcTransactions.byState {\n\t\tif candidate.Completed {\n\t\t\tcontinue\n\t\t}\n\t\tif transaction.ClientIP != \"\" && candidate.ClientIP == transaction.ClientIP {\n\t\t\tperIP++\n\t\t}\n\t\tif transaction.Binding != \"\" && candidate.Binding == transaction.Binding {\n\t\t\tperBinding++\n\t\t}\n\t}\n\tif perIP >= oidcTransactionPerIP || perBinding >= oidcTransactionPerBind {\n\t\treturn errors.New(\"too many pending OIDC login transactions\")\n\t}\n\toidcTransactions.byState[transaction.State] = transaction\n\tif transaction.PollToken != \"\" {\n\t\toidcTransactions.byPoll[transaction.PollToken] = transaction.State\n\t}\n\treturn nil\n}\n\nfunc claimOIDCTransaction(ctx context.Context, state, binding string,\n\tallowDesktopWithoutBinding bool) (*oidcTransaction, bool, error) {\n\tif state == \"\" {\n\t\treturn nil, false, errors.New(\"OIDC state is missing\")\n\t}\n\toidcTransactions.Lock()\n\tcleanupOIDCTransactionsLocked()\n\ttransaction := oidcTransactions.byState[state]\n\tif transaction == nil {\n\t\toidcTransactions.Unlock()","sourceCodeStart":652,"sourceCodeEnd":688,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/oidc.go#L652-L688","documentation":"storeOIDCTransaction also enforces per-client rate limits: at most oidcTransactionPerIP pending (non-completed) transactions from the same client IP, and at most oidcTransactionPerBind transactions sharing the same binding (desktop window binding). When either limit is reached, the new login attempt is rejected with this error. This stops a single client or desktop instance from flooding the pending-login table.","triggerScenarios":"OIDCStart or OIDCValidateStart from an IP that already has oidcTransactionPerIP pending transactions, or a desktop/validate flow whose binding already has oidcTransactionPerBind pending transactions.","commonSituations":"A user repeatedly clicking 'Sign in with OIDC' without finishing any login; a corporate NAT sharing one IP among many users; a desktop app that restarted with pending transactions still counted.","solutions":["Complete or wait for the timeout of the already-pending login attempts, then retry","Clear pending transactions by restarting the kernel or waiting for oidcTransactionTimeout expiry","Deploy a reverse-proxy rate limiter or raise oidcTransactionPerIP/oidcTransactionPerBind if many users legitimately share an egress IP"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := storeOIDCTransaction(tx); err != nil {\n    if strings.Contains(err.Error(), \"too many pending\") {\n        // wait for the existing pending transactions to expire, then retry once\n        time.Sleep(oidcTransactionTimeout)\n        err = storeOIDCTransaction(tx)\n    }\n}","preventionTips":["Click 'Sign in with OIDC' once per attempt and finish the flow","Avoid many users behind one NAT IP initiating logins simultaneously, or raise oidcTransactionPerIP","Clear pending state by restarting the kernel if limits are stuck"],"tags":["oidc","rate-limit","per-ip"],"backgroundTag":"rate-limit-exceeded","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"}