{"record":{"id":"5e0ba046af45fdee","repo":"siyuan-note/siyuan","slug":"oidc-login-transaction-capacity-reached","errorCode":null,"errorMessage":"OIDC login transaction capacity reached","messagePattern":"OIDC login transaction capacity reached","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/oidc.go","lineNumber":655,"sourceCode":"\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn &oidcTransaction{State: state, Nonce: nonce, CodeVerifier: verifier, PollToken: pollToken, Binding: binding,\n\t\tClientIP: clientIP,\n\t\tFlow:     input.Flow, RedirectURL: redirectURL, To: input.To, ConfigVersion: oidcConfigurationVersion(Conf.GetOIDC()), RememberMe: input.RememberMe,\n\t\tExpiresAt: time.Now().Add(oidcTransactionTimeout), Done: make(chan struct{})}, nil\n}\n\nfunc storeOIDCTransaction(transaction *oidcTransaction) error {\n\toidcTransactions.Lock()\n\tdefer oidcTransactions.Unlock()\n\tcleanupOIDCTransactionsLocked()\n\tif transaction.Done == nil {\n\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 != \"\" {","sourceCodeStart":637,"sourceCodeEnd":673,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/oidc.go#L637-L673","documentation":"storeOIDCTransaction registers a new pending OIDC login transaction and enforces a global capacity limit of oidcTransactionMax entries in the byState map (after expiring stale entries). When the map is at capacity, new logins are rejected with this error to bound memory usage and prevent state-forgery resource exhaustion.","triggerScenarios":"OIDCStart or OIDCValidateStart attempts to create a login transaction while oidcTransactions.byState already holds oidcTransactionMax entries even after cleanupOIDCTransactionsLocked removed expired/completed ones.","commonSituations":"Login-flood/DoS traffic hitting the OIDC start endpoint; many abandoned desktop/validate login tabs that never complete; a burst of mobile login attempts.","solutions":["Wait for existing transactions to expire (oidcTransactionTimeout) or complete, then retry the login","Reduce load: close abandoned login tabs and investigate the source of the request flood (rate-limit at a reverse proxy)","If legitimate capacity is chronously exceeded, raise oidcTransactionMax in the source or shorten oidcTransactionTimeout"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := storeOIDCTransaction(tx); err != nil {\n    if strings.Contains(err.Error(), \"capacity reached\") {\n        time.Sleep(time.Second) // let expired entries be cleaned\n        err = storeOIDCTransaction(tx)\n    }\n}","preventionTips":["Don't script floods against the OIDC start endpoint","Close abandoned login tabs/windows","Rate-limit OIDC start requests at the reverse proxy"],"tags":["oidc","capacity-limit","dos-protection"],"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"}