siyuan-note/siyuan · warning

wait for OIDC login transaction failed

Error message

wait for OIDC login transaction failed: %w

What it means

When a transaction has already been claimed (e.g. the desktop app polling via poll token and the callback both claiming it), the second claimant waits on transaction.Done for the flow to complete. If the caller's context is cancelled or times out first, this error wraps the context error via %w. It signals that the wait was aborted, not that the login itself failed.

Solutions

  1. Retry the claim/wait with a fresh context if the login is still in progress
  2. Increase the client-side request timeout so the poll waits at least as long as oidcTransactionTimeout
  3. Ensure the UI keeps the connection alive until the transaction completes or expires

Example fix

// before
ctx := context.Background() // or a short-lived request ctx
// after: give the wait enough headroom
ctx, cancel := context.WithTimeout(context.Background(), oidcTransactionTimeout)
defer cancel()
Defensive patterns

Strategy: retry

Try / catch

tx, done, err := claimOIDCTransaction(ctx, state, binding, false)
if err != nil {
    var ctxErr error
    if errors.As(err, &ctxErr) || strings.Contains(err.Error(), "context") {
        // wait aborted: retry with a longer-lived context if login may still complete
    }
}

Prevention

When it happens

Trigger: A second concurrent claim of the same state (poll endpoint vs callback) whose request context is cancelled — HTTP client disconnect, request timeout, or explicit context cancellation before the other claimant completes the transaction.

Common situations: Desktop poll request aborted by the user closing the window; frontend fetch timeout shorter than the login takes; user refreshing/cancelling while another tab waits.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/8ee0af86490bb30d. Report an issue: GitHub.

Appendix: source

Thrown at kernel/model/oidc.go:712

		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 &copy, false, nil
	}
	done := transaction.Done
	oidcTransactions.Unlock()

	select {
	case <-ctx.Done():
		return nil, false, fmt.Errorf("wait for OIDC login transaction failed: %w", ctx.Err())
	case <-done:
	}

	oidcTransactions.Lock()
	defer oidcTransactions.Unlock()
	transaction = oidcTransactions.byState[state]
	if transaction == nil || !transaction.Completed {
		return nil, false, errors.New("OIDC login transaction was not found or has expired")
	}
	copy := *transaction
	return &copy, true, nil
}

func completeOIDCTransaction(state string, success bool, message string) {
	oidcTransactions.Lock()
	defer oidcTransactions.Unlock()
	transaction := oidcTransactions.byState[state]
	if transaction == nil {

View on GitHub (pinned to 9f775e8a12)