{"record":{"id":"0099d1da6df3b2e5","repo":"hyperledger/fabric","slug":"expected-verification-sequence-d-but-proposal-ha","errorCode":null,"errorMessage":"expected verification sequence %d, but proposal has %d","messagePattern":"expected verification sequence (.+?), but proposal has (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/consensus/smartbft/verifier.go","lineNumber":117,"sourceCode":"func (v *Verifier) VerifyProposal(proposal types.Proposal) ([]types.RequestInfo, error) {\n\tblock, err := ProposalToBlock(proposal)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trtc := v.RuntimeConfig.Load().(RuntimeConfig)\n\tif err := verifyHashChainAndDataHash(block, rtc.LastCommittedBlockHash); err != nil {\n\t\treturn nil, err\n\t}\n\n\trequests, err := v.verifyBlockDataAndMetadata(block, proposal.Metadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tverificationSeq := v.VerificationSequence()\n\tif verificationSeq != uint64(proposal.VerificationSequence) {\n\t\treturn nil, errors.Errorf(\"expected verification sequence %d, but proposal has %d\", verificationSeq, proposal.VerificationSequence)\n\t}\n\n\treturn requests, nil\n}\n\n// RequestsFromProposal converts proposal to []RequestInfo\nfunc (v *Verifier) RequestsFromProposal(proposal types.Proposal) []types.RequestInfo {\n\tblock, err := ProposalToBlock(proposal)\n\tif err != nil {\n\t\treturn []types.RequestInfo{}\n\t}\n\n\tif block.Data == nil {\n\t\treturn []types.RequestInfo{}\n\t}\n\n\tvar res []types.RequestInfo\n\tfor _, txn := range block.Data.Data {","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/consensus/smartbft/verifier.go#L99-L135","documentation":"VerifyProposal compares the verifier's current verification sequence (the config sequence under which signatures are verified) with the sequence recorded in the proposal. A mismatch means the proposal was produced against a stale or newer channel config, so it is rejected.","triggerScenarios":"A node whose channel config has advanced (verification sequence bumped) receiving a proposal assembled by a leader still on the old config; partitioned or lagging consensus replicas during config updates.","commonSituations":"Config update (e.g. adding a consenter) committed by most nodes while one replica lags; leader election racing with a config change; replaying an old proposal after a config commit.","solutions":["Ensure all replicas commit the latest config block and catch up their ledger before participating","Trigger re-proposal/retry: the leader will reassemble the proposal under the current verification sequence","Check network connectivity of the lagging node so it receives config blocks promptly","Restart the lagging orderer after ledger catch-up to refresh its runtime config"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"seq := v.VerificationSequence()\nif seq != uint64(proposal.VerificationSequence) {\n    return fmt.Errorf(\"stale proposal: have %d want %d\", proposal.VerificationSequence, seq)\n}","typeGuard":null,"tryCatchPattern":"if err := VerifyProposal(proposal, ...); err != nil {\n    if strings.Contains(err.Error(), \"expected verification sequence\") {\n        // wait for ledger catch-up / let leader re-propose\n    }\n}","preventionTips":["Keep all replicas in sync on config blocks before proposals","Avoid replaying old proposals after config updates","Monitor replication lag across orderer nodes"],"tags":["hyperledger-fabric","smartbft","consensus","config-sequence"],"backgroundTag":"verification-sequence-mismatch","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}