siyuan-note/siyuan · error
OIDC login transaction capacity reached
Error message
OIDC login transaction capacity reached
What it means
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.
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
Defensive patterns
Strategy: retry
Try / catch
if err := storeOIDCTransaction(tx); err != nil {
if strings.Contains(err.Error(), "capacity reached") {
time.Sleep(time.Second) // let expired entries be cleaned
err = storeOIDCTransaction(tx)
}
} Prevention
- Don't script floods against the OIDC start endpoint
- Close abandoned login tabs/windows
- Rate-limit OIDC start requests at the reverse proxy
When it happens
Trigger: OIDCStart or OIDCValidateStart attempts to create a login transaction while oidcTransactions.byState already holds oidcTransactionMax entries even after cleanupOIDCTransactionsLocked removed expired/completed ones.
Common situations: Login-flood/DoS traffic hitting the OIDC start endpoint; many abandoned desktop/validate login tabs that never complete; a burst of mobile login attempts.
Related errors
- A loopback OIDC redirect URL is required for local access
- A public HTTPS OIDC redirect URL is required for remote…
- decode OIDC claims failed
- Desktop OIDC login requires a loopback listener
- discover OIDC provider failed
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/5e0ba046af45fdee.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/oidc.go:655
if err != nil {
return nil, err
}
}
return &oidcTransaction{State: state, Nonce: nonce, CodeVerifier: verifier, PollToken: pollToken, Binding: binding,
ClientIP: clientIP,
Flow: input.Flow, RedirectURL: redirectURL, To: input.To, ConfigVersion: oidcConfigurationVersion(Conf.GetOIDC()), RememberMe: input.RememberMe,
ExpiresAt: time.Now().Add(oidcTransactionTimeout), Done: make(chan struct{})}, nil
}
func storeOIDCTransaction(transaction *oidcTransaction) error {
oidcTransactions.Lock()
defer oidcTransactions.Unlock()
cleanupOIDCTransactionsLocked()
if transaction.Done == nil {
transaction.Done = make(chan struct{})
}
if len(oidcTransactions.byState) >= oidcTransactionMax {
return errors.New("OIDC login transaction capacity reached")
}
perIP, perBinding := 0, 0
for _, candidate := range oidcTransactions.byState {
if candidate.Completed {
continue
}
if transaction.ClientIP != "" && candidate.ClientIP == transaction.ClientIP {
perIP++
}
if transaction.Binding != "" && candidate.Binding == transaction.Binding {
perBinding++
}
}
if perIP >= oidcTransactionPerIP || perBinding >= oidcTransactionPerBind {
return errors.New("too many pending OIDC login transactions")
}
oidcTransactions.byState[transaction.State] = transaction
if transaction.PollToken != "" {View on GitHub (pinned to 9f775e8a12)