{"record":{"id":"f45baf681feaadfd","repo":"hyperledger/fabric","slug":"sequences-d-and-d-were-received-consecutively","errorCode":null,"errorMessage":"sequences %d and %d were received consecutively","messagePattern":"sequences (.+?) and (.+?) were received consecutively","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"orderer/common/cluster/util.go","lineNumber":250,"sourceCode":"\t\treturn err\n\t}\n\t// Verify data hash matches the hash in the header\n\tif !bytes.Equal(dataHash, block.Header.DataHash) {\n\t\tcomputedHash := hex.EncodeToString(dataHash)\n\t\tclaimedHash := hex.EncodeToString(block.Header.DataHash)\n\t\treturn errors.Errorf(\"computed hash of block (%d) (%s) doesn't match claimed hash (%s)\",\n\t\t\tseq, computedHash, claimedHash)\n\t}\n\t// We have a previous block in the buffer, ensure current block's previous hash matches the previous one.\n\tif indexInBuffer > 0 {\n\t\tprevBlock := blockBuff[indexInBuffer-1]\n\t\tcurrSeq := block.Header.Number\n\t\tif prevBlock.Header == nil {\n\t\t\treturn errors.New(\"previous block header is nil\")\n\t\t}\n\t\tprevSeq := prevBlock.Header.Number\n\t\tif prevSeq+1 != currSeq {\n\t\t\treturn errors.Errorf(\"sequences %d and %d were received consecutively\", prevSeq, currSeq)\n\t\t}\n\t\tif !bytes.Equal(block.Header.PreviousHash, protoutil.BlockHeaderHash(prevBlock.Header)) {\n\t\t\tclaimedPrevHash := hex.EncodeToString(block.Header.PreviousHash)\n\t\t\tactualPrevHash := hex.EncodeToString(protoutil.BlockHeaderHash(prevBlock.Header))\n\t\t\treturn errors.Errorf(\"block [%d]'s hash (%s) mismatches block [%d]'s prev block hash (%s)\",\n\t\t\t\tprevSeq, actualPrevHash, currSeq, claimedPrevHash)\n\t\t}\n\t}\n\treturn nil\n}\n\n// VerifyBlockSignature verifies the signature on the block with the given BlockVerifier and the given config.\nfunc VerifyBlockSignature(block *common.Block, verifier protoutil.BlockVerifierFunc) error {\n\treturn verifier(block.Header, block.Metadata)\n}\n\n// EndpointCriteria defines criteria of how to connect to a remote orderer node.\ntype EndpointCriteria struct {","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/orderer/common/cluster/util.go#L232-L268","documentation":"VerifyBlockHash enforces that consecutive blocks in the buffer have strictly contiguous sequence numbers: prevBlock.Header.Number + 1 must equal block.Header.Number. This error is returned when blocks N and N+1 positions in the buffer hold sequence numbers that are not consecutive, indicating a gap or a duplicated/out-of-order block. It guards the block replication pipeline against silently accepting a discontinuous chain.","triggerScenarios":"Calling cluster.VerifyBlockHash(indexInBuffer, blockBuff) where blockBuff[indexInBuffer-1].Header.Number + 1 != blockBuff[indexInBuffer].Header.Number — e.g., buffer contains blocks with sequences [5, 7] or [5, 5].","commonSituations":"A BlockPuller pulled blocks from a lagging/misbehaving orderer; an operator manually copied or truncated blocks in the ledger directory; buffers assembled from mixed sources (file + network) resulting in gaps; test code that hand-builds blocks with wrong sequence numbers.","solutions":["Verify the buffer contents: print Header.Number of all blocks and identify the gap or duplicate.","If blocks were pulled from a peer/orderer, re-pull from a different node whose ledger is known-good (e.g., re-create the BlockPuller with a different endpoint).","Ensure blocks are appended to the buffer strictly in sequence order; sort or re-request missing sequences before verifying.","If the local ledger has gaps, resync from genesis or from the latest config block pulled from the ordering service.","In tests, generate blocks with protoutil utilities that increment Header.Number correctly."],"exampleFix":"// before\nbuffer := []*common.Block{block5, block7} // gap\nerr := cluster.VerifyBlockHash(1, buffer) // sequences 5 and 7 were received consecutively\n// after\nbuffer := []*common.Block{block5}\nblock6, err := puller.PullBlock(6) // fetch the missing block\nbuffer = append(buffer, block6, block7)\nerr = cluster.VerifyBlockHash(2, buffer)","handlingStrategy":"validation","validationCode":"for i := 1; i < len(blockBuff); i++ {\n    if blockBuff[i].Header.Number != blockBuff[i-1].Header.Number+1 {\n        return fmt.Errorf(\"gap between seq %d and %d\", blockBuff[i-1].Header.Number, blockBuff[i].Header.Number)\n    }\n}\nerr := cluster.VerifyBlockHash(len(blockBuff)-1, blockBuff)","typeGuard":"func sequencesContiguous(buff []*common.Block) bool {\n    for i := 1; i < len(buff); i++ {\n        if buff[i].Header == nil || buff[i-1].Header == nil || buff[i].Header.Number != buff[i-1].Header.Number+1 { return false }\n    }\n    return true\n}","tryCatchPattern":"if err := cluster.VerifyBlockHash(idx, blockBuff); err != nil {\n    var prevSeq, currSeq uint64\n    if _, scanErr := fmt.Sscanf(err.Error(), \"sequences %d and %d were received consecutively\", &prevSeq, &currSeq); scanErr == nil {\n        // re-pull block at currSeq from another orderer, then retry verification\n    }\n    return err\n}","preventionTips":["Only append blocks to the buffer in strictly increasing sequence order","Re-request missing sequences instead of skipping over gaps","Do not mix blocks from multiple orderers/sources in one buffer without validation","Monitor puller logs for skipped or retried sequence numbers"],"tags":["blockchain","hyperledger-fabric","orderer","sequence-gap","consensus"],"backgroundTag":"block-sequence-gap","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"}