siyuan-note/siyuan · error
OIDC login transaction was not found or has expired
Error message
OIDC login transaction was not found or has expired
What it means
claimOIDCTransaction looks up the pending transaction by state after cleaning up expired entries. If no transaction with that state exists — either it expired after oidcTransactionTimeout, was never created, or was already removed — the callback cannot be matched to a login and this error is returned.
Solutions
- Restart the OIDC login from scratch — a fresh state will be issued
- Complete the IdP login promptly, within oidcTransactionTimeout
- If running multiple instances, enable sticky sessions or a shared transaction store so start and callback hit the same node
Defensive patterns
Strategy: try-catch
Try / catch
tx, done, err := claimOIDCTransaction(ctx, state, binding, false)
if err != nil {
// treat as expired: redirect the user to restart the OIDC login
redirectToLoginStart(w, r)
return
} Prevention
- Complete the IdP login promptly, within the transaction timeout
- Avoid restarting the kernel mid-login
- Use sticky sessions or a single instance for OIDC flows
When it happens
Trigger: OIDCCallback/OIDCMobileCallback arrives with a state value that is not in oidcTransactions.byState after cleanupOIDCTransactionsLocked: expired login, server restart, state from a different instance, or double-claimed then deleted transaction.
Common situations: User leaves the IdP login page open longer than the transaction timeout, then completes it; kernel restarted between login start and callback; load-balanced setup sending start and callback to different nodes.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- OIDC validation transaction was not found or has expired
- A loopback OIDC redirect URL is required for local access
- A public HTTPS OIDC redirect URL is required for remote…
- attribute view is not unused
- block swap notebook has changed
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/a75b72d1bef2e9fb.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/model/oidc.go:689
}
oidcTransactions.byState[transaction.State] = transaction
if transaction.PollToken != "" {
oidcTransactions.byPoll[transaction.PollToken] = transaction.State
}
return nil
}
func claimOIDCTransaction(ctx context.Context, state, binding string,
allowDesktopWithoutBinding bool) (*oidcTransaction, bool, error) {
if state == "" {
return nil, false, errors.New("OIDC state is missing")
}
oidcTransactions.Lock()
cleanupOIDCTransactionsLocked()
transaction := oidcTransactions.byState[state]
if transaction == nil {
oidcTransactions.Unlock()
return nil, false, errors.New("OIDC login transaction was not found or has expired")
}
if transaction.ConfigVersion != oidcConfigurationVersion(Conf.GetOIDC()) {
deleteOIDCTransactionLocked(state)
oidcTransactions.Unlock()
return nil, false, errors.New("OIDC configuration changed during login")
}
if !(allowDesktopWithoutBinding && (transaction.Flow == oidcFlowDesktop || transaction.Flow == oidcFlowValidate)) &&
(binding == "" || binding != transaction.Binding) {
oidcTransactions.Unlock()
return nil, false, errors.New("OIDC login binding does not match")
}
if !transaction.Claimed {
transaction.Claimed = true
copy := *transaction
oidcTransactions.Unlock()
return ©, false, nil
}
done := transaction.DoneView on GitHub (pinned to 9f775e8a12)