{"record":{"id":"56b7ce9c911ef6b7","repo":"siyuan-note/siyuan","slug":"oidc-validation-configuration-is-missing","errorCode":null,"errorMessage":"OIDC validation configuration is missing","messagePattern":"OIDC validation configuration is missing","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/oidc.go","lineNumber":780,"sourceCode":"\tcopy := *transaction\n\treturn &copy, true\n}\n\nfunc activateOIDCValidation(pollToken, binding string) (activated bool, err error) {\n\toidcTransactions.Lock()\n\tdefer oidcTransactions.Unlock()\n\tcleanupOIDCTransactionsLocked()\n\tstate := oidcTransactions.byPoll[pollToken]\n\ttransaction := oidcTransactions.byState[state]\n\tif transaction == nil || transaction.Flow != oidcFlowValidate || transaction.Binding == \"\" ||\n\t\tbinding == \"\" || transaction.Binding != binding || !transaction.Completed || !transaction.Success {\n\t\treturn false, errors.New(\"OIDC validation transaction was not found or has expired\")\n\t}\n\tif transaction.Activated {\n\t\treturn false, nil\n\t}\n\tif transaction.Config == nil {\n\t\treturn false, errors.New(\"OIDC validation configuration is missing\")\n\t}\n\tconfigurationChanged, swapped := Conf.CompareAndSetOIDC(transaction.ConfigVersion, transaction.Config)\n\tif !swapped {\n\t\tdeleteOIDCTransactionLocked(state)\n\t\treturn false, errors.New(\"OIDC configuration changed during validation\")\n\t}\n\ttransaction.Config = nil\n\ttransaction.Activated = true\n\treturn configurationChanged, nil\n}\n\nfunc cancelOIDCValidation(pollToken, binding string) bool {\n\toidcTransactions.Lock()\n\tdefer oidcTransactions.Unlock()\n\tcleanupOIDCTransactionsLocked()\n\tstate := oidcTransactions.byPoll[pollToken]\n\ttransaction := oidcTransactions.byState[state]\n\tif transaction == nil || transaction.Flow != oidcFlowValidate || transaction.Activated || transaction.Binding == \"\" ||","sourceCodeStart":762,"sourceCodeEnd":798,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/oidc.go#L762-L798","documentation":"During activation of a validated OIDC configuration, the transaction record must still carry the candidate config snapshot (transaction.Config). If Config is nil the candidate configuration cannot be applied, so activateOIDCValidation aborts. This indicates internal state corruption or a transaction whose config was already consumed.","triggerScenarios":"Calling OIDCValidateActivate on a transaction that went through finishOIDCExchange as a non-validate flow, a transaction whose Config was already set to nil after a previous successful activation (Activated handled earlier, but ordering/nil paths in tests), or a manually constructed/corrupted transaction in tests TestActivateOIDCValidationAppliesCandidateOnce/RejectsChangedConfiguration/CancelOIDCValidationPreventsActivation.","commonSituations":"Race where activation runs twice concurrently and the second call sees a mutated transaction; custom test harness builds a validate-flow transaction without attaching Config; memory corruption after partial failure during exchange.","solutions":["Recreate the validation transaction via OIDCValidateStart so a fresh candidate config snapshot is attached","Ensure finishOIDCExchange ran for the validate flow and attached transaction.Config before activation","Audit concurrent activation paths — guard against a second activation operating on the same transaction","Check the transaction's Flow is oidcFlowValidate; a login-flow transaction has no candidate Config"],"exampleFix":"// before\ntransaction := &oidcTransaction{Flow: oidcFlowValidate, Binding: b, Completed: true, Success: true}\n// after\ntransaction := &oidcTransaction{Flow: oidcFlowValidate, Binding: b, Completed: true, Success: true, Config: candidateConf, ConfigVersion: ver}","handlingStrategy":"type-guard","validationCode":"// ensure the validate transaction carries a candidate config before activating\nif txn.Flow == oidcFlowValidate && txn.Config == nil { return ErrNoCandidateConfig }","typeGuard":"func hasCandidateConfig(txn *oidcTransaction) bool {\n    return txn != nil && txn.Flow == oidcFlowValidate && txn.Config != nil\n}","tryCatchPattern":"ok, err := model.OIDCValidateActivate(pollToken, binding)\nif err != nil && strings.Contains(err.Error(), \"configuration is missing\") {\n    return restartOIDCValidation() // rebuild transaction with attached config\n}","preventionTips":["Always obtain transactions via OIDCValidateStart, never construct them by hand","Complete the token exchange (finishOIDCExchange) before activation so Config is populated","Avoid concurrent activations of the same transaction"],"tags":["oidc","auth","config","invariant"],"backgroundTag":"missing-configuration","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"}