siyuan-note/siyuan · error
OIDC state is missing
Error message
OIDC state is missing
What it means
claimOIDCTransaction received an empty state string. The state correlates the IdP callback to the stored transaction; without it nothing can be looked up and the request is rejected immediately before any locking.
Source
Thrown at kernel/model/oidc.go:738
}
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 != "" {
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")
}View on GitHub (pinned to 251596fc0d)
Solutions
- Restart the OIDC flow via /api/system/oidc/start to obtain a fresh state.
- Verify the IdP redirect configuration preserves the state parameter end to end.
- Check the client passes the state through unchanged from start to callback.
Defensive patterns
Strategy: validation
Validate before calling
// Reject callbacks/polls with no state before reaching the kernel's claim path.
if c.Query("state") == "" {
return errors.New("missing state parameter")
} Prevention
- Always begin a flow with /api/system/oidc/start to obtain a state.
- Verify the IdP preserves the state parameter in its redirect.
- Pass state through the client unchanged from start to callback.
When it happens
Trigger: A request reaches /api/system/oidc/callback or the desktop/validate poll without a state query/body parameter; the IdP dropped state from its redirect; a manual/curled callback omitted it.
Common situations: IdP mis-configured to strip state; client bug losing the parameter between start and callback; direct browser hit on the callback URL.
Related errors
- OIDC login transaction was not found or has expired
- OIDC login binding does not match
- OIDC authorization code is missing
- Save OIDC login session failed
- OIDC configuration is missing
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/47089e6b3dbee950.
Report an issue: GitHub.